[우아한테크코스 6기 프리코스] 1주차 회고 - 숫자 야구미션

오현재·2023년 10월 27일
post-thumbnail

이 글은 우아한테크코스 프론트엔드 6기 의 프리코스 에서 1주 단위로 미션을 진행하며 느낀 점을 회고한 글입니다. 코드 리뷰와 커뮤니티가 있는 미션 특성상 다른 참가자분들을 언급할 일이 많아, 편의상 “크루원” 라고 칭할 것인데, 이점 참고 부탁드립니다!

💬 들어가며

우테코 과제 제출 페이지에 소감문 을 작성하는 곳이 따로 있는 것 을 보고, 돌아보는 것에 대한 가치를 다시 생각해보게 되었다. 그래서 나도 그 여정을 1주 단위의 회고로 남겨보려고한다.

회고는 미션의 의도 를 생각해보기 , 회고는 미션을 혼자 진행 하며, 종료 후 다른 개발자의 코드를 보고 리뷰 를 하며, 한 미션의 프로세스를 끝내며 내가 배운 점, 느낀 점, 들은 생각 들 위주로 작성할 것 같다!

특히 혼자 먼저 진행하며 들었던 생각들은 부족해 보이는 부분들이 많을 것으로 예상되어.. 만약 의견이 있으시다면 댓글로 남겨주시거나, 감안하여 읽어주시면 감사하겠습니다!!

⚾ 미션 진행 과정

이번 미션은 숫자 야구 게임이다.
기본적으로 플레이어가 상대방(컴퓨터) 가 생성한 1부터 9까지 서로 다른 수로 이루어진 3자리의 수를 맞추는 게임이다. 같은 수, 같은 자리 는 스트라이크, 다른 자리 는 볼, 같은 수가 전혀 없으면 낫싱이란 힌트를 얻고, 그 힌트를 이용해 먼저 상대방(컴퓨터) 의 수를 맞추면 승리한다.

https://github.com/woowacourse-precourse/javascript-baseball-6

✅ 요구사항

  • 미션은 기능 요구 사항, 프로그래밍 요구 사항, 과제 진행 요구 사항 세 가지로 구성되어 있다.
  • 세 개의 요구 사항을 만족하기 위해 노력한다. 특히 기능을 구현하기 전에 기능 목록 을 만든다.
  • 기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현 한다.

요구사항을 보고 들었던 생각은 “실제 현장에서 만들게 될 프로덕트의 요구사항 을 상세히 풀어 쓰면 이러한 형태가 되지않을 까?” 이었다. 예를 들면 프로그래밍 요구 사항 에서 특정 Node 버전에서 작동해야 한다든지, 사용할 API 를 제한해 준다든지 하는 점 들 이었다.

또 요구 사항 외의 내용을 생각해야 한다는 점은, 결국 요구사항도 프로그램 완성의 수단이지, 궁극적인 목표는 이 프로그램을 완벽하게 완성하는 것 이라는 생각이 들게 했다. 예를 들면 예외 사항의 조건 들도, 미션에서 제공하는 것 외의 조건이 있다면, 분명 처리해야 할 것이다. (하지만 나는 빼놓은 게 있었다..)

요구사항을 다 읽어보고 미션 진행을 위한 첫번째 단계인 기능 목록을 작성했다.

📒 기능 목록

기능 목록을 작성하며 바로 들은 생각은 “하나의 애플리케이션 을 이해할 때, 직접 적는 것이 좋은 방식이 될 수 있구나” 라는 것이었다. 사실 개발 전에 기능 목록을 작성한 적은 이번 우테코 프리코스를 진행하며 처음 해본 경험이었다.

그럼 뭘 기준으로 개발 했었냐면, 보통 그 프로덕트의 추상적인 이미지를 생각했던 것 같다. 한마디로 명확하게 표현이 되지 않는 지점은 “이럴 것이다” 라고 생각하며 개발했다. 반면, 기능 목록은 명확한 기준과 의도를 가지고 개발하는 것에 큰 도움 을 주었다.

또 좋았던 점은, 기능을 작성하고 분류 하는 과정에서 애플리케이션 을 어떻게 설계 할지에 대한 생각으로 자연스레 이어졌던 것 이다. 나의 경우 플레이어, 컴퓨터, 유효성 검증 이라는 카테고리로 분류했다.

여기서 조금 아쉬웠던 것은, 기능 분류 에 많은 고민을 하지 않았던 것이다. 나의 경우 이 기능 분류에 따라 생성한 객체 중심으로 구현하다보니, 관심사 분리 측면에서 헷갈릴 때가 많았다. 다음 미션 에서는 굳이 분류를 하지 않거나, 구현을 진행해보며 병행하여 분류하는 방식으로 진행해 볼 생각이다.

마지막으로, 커밋을 하는 명확한 단위가 생긴 점 이 좋았다. 이전의 프로덕트들에서 날리던 나의 커밋 메세지를 생각하면, “코드의 변경사항과 그에 따른 변화” 를 기록하는 것에 불과하지 않았나 싶다. 하지만 기능 단위 커밋을 생성하니, 커밋을 나누고, 메세지를 작성하는 과정이 명확하고 편해졌다.

🤔 혼자 고민한 점

1️⃣ 클래스 vs 객체

내 숫자 야구 게임은 크게 플레이어, 컴퓨터, 유효성 검증 라는 관심사로 나누어져있다. 객체를 중심으로 생각하다보니 처음에 클래스를 생성했다.

// 컴퓨터
export class Computer {
  answer = [];
  count = { ball: 0, strike: 0 };

	/* ... */
}

클래스는 객체를 만들어내는 템플릿 이다. 그런 관점에서 본다면, 여러 객체를 만들어 낼때 적합한 클래스는 숫자 야구 게임에서는 필요 없었다. 더군다나, 플레이를 반복할 수 있는 특성상, 그때마다 클래스를 생성한다면 낭비라고 생각했다. 그래서 클래스로 작성했던 코드들을 모두 객체로 수정했다.

const computer = {
  answer: [],
  count: { ball: 0, strike: 0 }

	/* ... */
}

문제는 answer, count 와 같이 외부에서 접근이 가능 하면 안되는 속성 들이 접근 및 수정이 가능했다는 것이었다. 해당 기능은 클래스의 private 키워드 로 해결할 수 있었다. 결국 다시 클래스로 롤백했다. 하지만, 이전과 다른 점은 은닉할 데이터가 있는 컴퓨터 만 클래스로 다시 수정했다는 것이다.

export class Computer {
  #answer = []; /** 외부 접근을 막아주는 private 멤버로 수정*/
  #count = { ball: 0, strike: 0 };

	/* ... */
}

구현을 다 하고 나서 든 생각인데, 조금은 불필요했던(Overthinking 같은) 고민이 아니었나? 하는 생각이 들었다. 물론 클래스가 템플릿이라는 점에서는 여러 객체를 생성 하기도 하지만, 그것 보다는 객체의 속성과 메서드를 추상화하는 청사진의 의미가 더 크기 때문에, 그러한 관점에서 내가한 고민은 불필요할 수 있었다는 생각이 든다.

2️⃣ 유효성 검증 리팩토링

이번 미션에서 가장 많이 썼다, 고쳤다 반복했던 부분인 것 같다. 숫자 게임 미션 에는 사용자 입력에 대한 다양한 예외 조건들이 있었다. 예외 조건에 따라 에러를 throw 하여 처리하는 코드는 조건문이 반복되는 형태였다. 그래서 ”중복을 없애고 더 짧은 코드로 표현할 수 없을까?” 라는 생각이 들었다.

export class Validation {
  #errorMessage = "";

  constructor(input) {
    this.input = input;
  }

  get invalidConditions() {
    const inputArray = this.input.split("");
    return new Map([
      [Number.isNaN(parseInt(this.input)), "문자를 입력했습니다."],
      [this.input.length !== 3, "입력된 숫자의 개수가 초과/미달 입니다."],
      [
        parseInt(this.input) &&
          inputArray.findIndex(
            (item) => inputArray.indexOf(item) !== inputArray.lastIndexOf(item)
          ) !== -1,
        "중복된 입력이 있습니다.",
      ],
    ]);
  }

  validate() {
    if (this.invalidConditions.has(true)) {
      this.setErrorMessage(this.invalidConditions.get(true));
      throw new Error(this.#errorMessage);
    }

    return true;
  }

  setErrorMessage(error) {
    this.#errorMessage = `[ERROR] ${error} 서로 다른 3 자리 의 숫자를 입력해주세요.`;
  }
}

예외 조건을 key 로, 그에 맞는 메세지를 value 로 가지는 Map 을 반환하는 invalidConditions 을 만들고, validate 메서드 에서는 Mapkeytrue 인지 확인해, true 라면 value 를 반환하는 작업만 한다. 처음엔 Map 이 아닌 배열에 저장하려 했지만, 속도가 더 빠른 Map 을 사용했다.

분명 반복되는 조건문은 사라졌다. 그런데 문제가 생겼다.

  1. Map 객체의 get 메서드가 반환하는 값은 순서가 보장되지 않는 것이었다. 당연할 것이 조건이 겹칠 경우 key 가 중복되기 때문이다. 예를 들어 입력으로 “string” 이라는 문자열 이 들어가면

    첫 번째 조건에 해당되어, "문자를 입력했습니다." 라는 메세지가 출력되어야 하는데, 세 번째 조건에도 해당되어, "중복된 입력이 있습니다." 라는 잘못된 메세지가 출력되는 것이었다.

  2. 숫자 야구 게임 미션에서 플레이어 입력은 두 가지로 나뉜다. 게임 플레이에 입력하는 3가지 숫자, 재시작/종료를 뜻하는 1 혹은 2. 위의 경우에서는 두 케이스가 혼용되어 정확한 에러를 throw 하지 못했다.

  3. key 와 value 가 뜻하는 것이 무엇인지 한번에 알아보기 어려웠다.

위의 이유들 때문에 다시 코드를 수정했다. 추가적으로 errorMessage, input 속성이 불필요하다고 느껴져 제거하고, 예외 조건과 에러 메세지 들을 상수화 했다.

const validations = (input) => {
  const inputArray = input.split("");
  return {
    NOT_NUMBER: {
      CONDITION: Number.isNaN(parseInt(input)),
      MESSAGE: "문자를 입력했습니다.",
    },
    OVER_OR_UNDER_LIMIT: {
      CONDITION: input.length !== 3,
      MESSAGE: "입력된 숫자의 개수가 초과/미달 입니다.",
    },
    DUPLICATED: {
      CONDITION:
        inputArray.findIndex(
          (item) => inputArray.indexOf(item) !== inputArray.lastIndexOf(item)
        ) !== -1,
      MESSAGE: "중복된 입력이 있습니다.",
    },
    UNDEFINED: {
      CONDITION: input !== "1" && input !== "2",
      MESSAGE: "입력값을 확인할 수 없습니다. 종료하겠습니다.",
    },
  };
};

export const validation = {
  validatePlayNumber(input) {
    const { NOT_NUMBER, OVER_OR_UNDER_LIMIT, DUPLICATED } = validations(input);

    if (NOT_NUMBER.CONDITION) {
      throw new Error(this.errorMessage(NOT_NUMBER.MESSAGE));
    }
    if (OVER_OR_UNDER_LIMIT.CONDITION) {
      throw new Error(this.errorMessage(OVER_OR_UNDER_LIMIT.MESSAGE));
    }
    if (DUPLICATED.CONDITION) {
      throw new Error(this.errorMessage(DUPLICATED.MESSAGE));
    }
  },

  validateSelectReplayOrExit(input) {
    const { UNDEFINED } = validations(input);
    if (UNDEFINED.CONDITION) {
      throw new Error(UNDEFINED.MESSAGE);
    }
  },

  errorMessage(error) {
    return `[ERROR] ${error} 서로 다른 3 자리 의 숫자를 입력해주세요.`;
  },
};

throw 문이 중복되지만, 훨씬 가독성이 좋다고 느껴졌다. 중복을 제거하는 것이 코드의 품질을 향상시키는 것에 중요 하지만, 읽기 좋고 이해하기 쉬운 코드도 중요하다는 걸 느꼈다.

😮 함께 자라기

PR 을 날리고 소감문을 부랴부랴 작성한 후 드디어 다른 크루원의 코드를 찾아보았다. 같은 프로젝트에 대한 무려 800개의 코드들이 있었다. 한분, 한분 코드를 열때마다 기대가 되었다. 대략 10 명의 정도 크루원들의 코드를 살펴보았다. 매우 흥미로웠고, 동시에 “정말 잘하는 사람이 많구나” 라고 생각했다.

내가 했던 고민들을 더 나은 방식으로 해결한 것들을 보게되었다. 혹은, 생각하지 못했던 부분을 발견했다. 가슴이 철렁거리기 보단 정말 즐거운 시간이었다.

1️⃣ 관심사 분리

파일과 디렉토리 구조를 통해 가장 처음 확인 한 부분. 평소 관심사 분리에 많이 신경쓰려고 노력하는 데, 정말 못했다는 생각이 들었다. 예를 들어, 아래는 컴퓨터(상대방) 역할을 하는 Computer 클래스 이다. 구현할 때는 몰랐지만, 다 만들고 보니 어쩐지 무거워 보인다. 결과 역할을 하는 #count 와 관련된 마지막 3개의 private 메서드를 중심으로 한번 보자.

export class Computer {
  #answer = []; // 정답
  #count = { ball: 0, strike: 0 }; // 결과 역할

  makeAnswer() {
    while (this.#answer.length < 3) {
      const number = Random.pickNumberInRange(1, 9);
      if (!this.#answer.includes(number)) {
        this.#answer.push(number);
      }
    }
  }

  async compareAnswerRepeatedly() {
    while (this.#count.strike !== 3) {
      this.#initializeCount();
      const playerInput = await player.getPlayNumber();
      this.#compareAnswer(playerInput);
      this.#printResult();
    }
    return true;
  }

  #initializeCount() {
    this.#count = { ...this.#count, strike: 0, ball: 0 };
  }

  #compareAnswer(input) {
    this.#answer.map((answer, index) => {
      if (input.includes(answer)) {
        let key = "ball";
        if (index === input.indexOf(answer)) {
          key = "strike";
        }
        this.#count = { ...this.#count, [key]: this.#count[key] + 1 };
      }
    });
  }

  #printResult() {
    const { strike, ball } = this.#count;
    if (strike === 0 && ball === 0) {
      Console.print("낫싱");
      return;
    }

    Console.print(
      `${ball > 0 ? `${ball}` : ""}${
        strike > 0 ? `${strike}스트라이크` : ""
      }`
    );

    if (strike === 3) {
      Console.print(COMPUTER_MESSAGE.COMPLETED);
    }
  }
}

결과 역할을 Computer 클래스내에 #count 속성과 관련된 메서드로 정의한 나와 달리, Score 라는 클래스로 독립적으로 생성한 방식으로 구현한 분이 있었다. 그렇게 분리하면, Computer 클래스 내의 #count 속성을 포함해, 그와 관련된 많은 메서드 를 제거 혹은 분리할 수 있었다!

감사의 코멘트 를 남기고, 수정에 들어갔다.

// Computer.js

export class Computer {
  #answer = [];
  #count = new Count();

  makeAnswer() {
    while (this.#answer.length < 3) {
      const number = Random.pickNumberInRange(1, 9);
      if (!this.#answer.includes(number)) {
        this.#answer.push(number);
      }
    }
  }

  async compareAnswer() {
    while (this.#count.strike !== 3) {
      this.#count = new Count(this.#answer);
      const playerInput = await player.getPlayNumber();
      this.#count.set(playerInput);
      this.#count.print();
    }
    return true;
  }
}

// Count.js

export class Count {
  ball = 0;
  strike = 0;

  constructor(answer) {
    this.answer = answer;
  }

  set(input) {
    this.answer.map((ans, index) => {
      if (input.includes(ans)) {
        let key = "ball";
        if (index === input.indexOf(ans)) {
          key = "strike";
        }
        this[key] += 1;
      }
    });
  }

  print() {
    if (this.strike === 0 && this.ball === 0) {
      Console.print("낫싱");
      return;
    }

    Console.print(
      `${this.ball > 0 ? `${this.ball}` : ""}${
        this.strike > 0 ? `${this.strike}스트라이크` : ""
      }`
    );

    if (this.strike === 3) {
      Console.print(COMPUTER_MESSAGE.COMPLETED);
    }
  }
}
  1. Computer 클래스에 있던 정답과 입력을 비교해 결과를 카운트, 출력하는 기능을 담당하는 #compareAnswer, #printResult 메서드 가 Count 클래스로 이동했다.
  2. Computer 클래스의 #count 속성 값이 Count 클래스의 인스턴스 로 바뀌면서 기존의 #count 속성을 초기화 해주던 #initializeCount 메서드 는 아예 필요가 없어져 제거했다.
  3. 메서드명 들도 더욱 의도가 명확하게 바뀌었다. compareAnswerCount 클래스의 set, compareAnswerRepeatedlycompareAnswer 로 깔끔하게 바뀌었다.

이로써 관심사가 분리되어 하나의 객체가 지니는 책임이 줄어들고, 불명확한 의도의 메서드명들도 명확하게 바뀌었다. 정말 만족스럽다.

2️⃣ 상수 처리 (feat. Object.freeze)

변하지 않는 값을 상수로 관리하는 것은 이전에 온보딩 프로그램에 참여했을 때, 부족함을 느끼고 신경쓰려고 노력하는 부분이었다. 그래서 잘 처리했다고 생각했지만, 다른 크루원들의 코드를 보니 놓친 부분이 많았다!

// ❌ before
return input === "1";

Console.print("낫싱");

// ✅ after
return input === INTERFACE.REPLAY

Console.print(COUNT.NOTHING);

위와 같이 모든 매직 넘버, 매직 스트링을 찾아 상수 변수에 할당했다. 추가적으로 상수화 할 때 Object 객체의 freeze 메서드를 사용한 크루원들이 많이 있었다.

Object.freeze 메서드는 객체를 동결 한다. 동결 이란 새로운 속성의 추가, 제거, 기존의 속성 의 변경 을 방지한다. 비슷한 기능으로 Object.seal 메서드 가 있다. 둘의 공통점은, 속성의 추가, 제거를 방지한다는 것. 차이점은 Object.seal 메서드는 기존의 속성의 변경이 가능하다. 수정 을 시도하면 보통 조용히 실패한다고 한다. 하지만, 예외적으로 엄격 모드 일 때는 TypeError 가 발생한다고 한다.

수정될 일이 없는 것을 보장함으로써 상수로서의 역할을 더 보강하고, 사이드 이펙트를 줄여주는 좋은 방법이라고 생각한다. 최종적으로 흩어져있는 상수들을 constants.js 파일로 모듈화 해주었다.

// constants.js

export const COMPUTER_MESSAGE = Object.freeze({
  START: "숫자 야구 게임을 시작합니다.",
  COMPLETED: "3개의 숫자를 모두 맞히셨습니다! 게임 종료",
});

export const INPUT_MESSAGE = Object.freeze({
  PLAY_NUMBER: "숫자를 입력해주세요 : ",
  SELECT_REPLAY_OR_EXIT: "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.",
});

/* 생략 ... */

3️⃣ 주석 과 JSDoc 활용

많은 크루원들이 메서드, 함수JSDoc 으로 타입 힌트를 제공하고, 간혹 주석으로 코드에 대한 설명을 덧붙이는 것을 보았다. JSDoc 으로 타입 힌트를 제공하는 코드는 리뷰에서도 좋은 평가를 받았던 것 같다. 나도 동의했다.

이전까지 JSDoc 을 일반적인 주석과 같은 용도로만 사용했고, 타입 힌트로서 사용된다는 것은 이번에 알게된 점이다. 이후 미션을 포함해 앞으로 적극 사용할 것 같다.

코드에 대한 설명을 주석으로 덧붙이는 것은 나도 고민했던 부분이었다. 특히, 요구사항 에 없는 부분을 구현할 때 “주석으로 설명을 붙이는 게 나을까?” 라는 생각이 들었다.

하지만 주석은 코드에 관련된 부분을 적지 않는 것이 좋다고 들은 기억이 났다. 그리고 변수 혹은 함수명 만으로 설명이 되는 코드를 짜고 싶어서 주석은 따로 달지 않았다. 이 부분에 대해서 2주차 시작날에 진행된 우테코 코스타와 1주차 공통 피드백에서 비슷한 답변을 받을 수 있었다.

의미 없는 주석을 달지 않는다. 변수 이름, 함수(메서드) 이름을 통해 어떤 의도인지가 드러난다면 굳이 주석을 달지 않는다. 모든 변수와 함수에 주석을 달기보다 가능하면 이름을 통해 의도를 드러내고, 의도를 드러내기 힘든 경우 주석을 다는 연습을 한다.

“변수, 함수명을 짓는 것이 개발할 때 제일 어렵다” 라는 오래된 격언에 동의하며, 이후 미션에서 최대한 의도가 명확한 변수, 함수명을 지으려고 노력해야겠다는 생각이 들었다. 그리고, 주석을 달지 않아서 헷갈릴 우려가 조금이라도 있다면, 다는 것이 좋을 것 같다는 생각도 들었다.

4️⃣ 요구사항 꼼꼼히 보기

나는 구현 할 때 ‘숫자 야구 게임을 시작합니다.’ 라는 문구가 게임이 시작될 때마다 출력되는 줄 알았다. 아니 사실 어느 시점에 출력되는 지 큰 관심이 없었다. 물론 출력되는 시점에 대해 기능목록에서 명시적으로 제공하지는 않는다. 하지만 예제를 조금만 면밀히 살펴보면 알수 있는 사실이었다.

숫자 야구 게임에서 중요한 기능은 숫자를 입력하고, 정답을 맞추는 과정일 것이다. 하지만 숫자 야구 게임 이라는 프로덕트를 제대로 완성했냐? 라는 관점에서 본다면, 숫자를 입력하고, 정답을 맞추는 과정 만 제대로 만들었다고 완성됐다고 볼 수는 없을 것이다.

사소한 부분까지 요구사항을 모두 충족시키는 것이 역시 중요하지 않나 라는 성찰을 하게 되는 꼼꼼한 크루원 분이 요구사항을 꼼꼼히 읽어보고 구현한 것을 보게된 코드 리뷰의 코멘트였다.

5️⃣ 정적 메서드

위의 클래스vs객체 섹션에서 했던 고민 중 하나는 정적 메서드 에 대해 잘 몰랐기 때문이라고 생각한다.

정적 메서드 는 클래스의 인스턴스 없이 호출이 가능하며 클래스가 인스턴스화되면 호출할 수 없다. 정적 메서드는 종종 어플리케이션의 유틸리티 함수를 만드는데 사용된다.

어떤 메서드들은 인스턴스 생성과 관계없이 사용되어야했는 데, 이것이 클래스가 아닌 객체를 사용하는 이유가 되기도 했다. 하지만 그 점은 정적 메서드 를 활용해여 쉽게 해결 할 수 있었다.

객체지향프로그래밍을 공부하고, 직접 구현해보고자 개인 프로젝트도 진행했는 데 이런 기초적인 지식이 아직 부족하다. “공부를 열심히 해야겠구나” 라는 생각이 든 부분 중 하나였다.

6️⃣ 스타일 가이드

Airbnb 의 스타일 가이드를 준수하는 것도 요구사항 이었는 데 고려하지 못했다. 다음 미션에서는 eslint 설정부터 시작할 거다.

7️⃣ constructor 의 역할

보통 constructor (생성자) 는 이렇게 사용된다.

class A {
	constructor(name){
		this.name = name;
	}
}

이전에 클래스의 인스턴스가 생성될 때 실행될 메서드를 constructor 안에 종종 넣곤 했는 데, 왠지 잘못 오용하고 있는 게 아닌 가 싶어 initialize 와 같은 초기화 역할을 하는 메서드를 따로 정의했다.

내가 오용하고 있는 게 아닌가? 생각하고 있는 방식으로 어떤 크루원이 코드를 작성한 것을 보았다. initialize 와 같은 방식을 제안하는 코멘트를 달기 직전 MDN 에 한번 검색해봤다.

constructor를 사용하면 다른 모든 메서드 호출보다 앞선 시점인, 인스턴스 객체를 초기화할 때 수행할 초기화 코드를 정의할 수 있다.

그렇다. 클래스의 속성의 값을 초기화 하는 것 말고도, 인스턴스 를 초기화 할때 수행할 필요가 있는 코드라면 정의할 수 있다고 한다. 그러니 해당 코드가 인스턴스 가 초기화 될때만, 그때마다 실행될 것인지에 대한 확신만 있으면 될 듯 하다. (코멘트 전의 검색은 옳은 선택이었다.)

🌄 미션을 마치며

나의 안타까운 실력을 바라보며

쉽지 않았던 것 같다. 테스트 코드, 노드 환경에서의 개발 등 익숙치 않은 것들이 많았다. 다른 크루원의 코드를 보고 이 회고를 작성 하면서도 자조적인 글을 쓰지않고 긍정적으로 생각하려고 노력했던 것 같다. 물론 즐겁고 흥미로운 감정이 훨씬 컸다. 하지만 내 실력에 대한 안타까움 은 어쩔 수 없이 남아있는 것 같다.

그 안타까움 들은 지금 미션을 개선하면서, 그리고 다음 미션에서 해소하려고 한다! 다른 크루원들의 코드를 보며 준비를 단단히 해두었다. 또 다음 미션에서 생기는 안타까움 은 그 다음 미션에서 해소하려고한다. 그렇게 생각하자.

코드 리뷰

위의 함께 자라기 는 모두 다른 크루원들의 코드를 보고 느꼈던 것을 모두 옮긴 것이다. 위에서도 말했듯 무려 800 개다. 그중 극히 일부만 보아도 개선되는 점이 많았다. 나는 너무 만족스러운 시간이었다. 일주일 정도 미션을 구현하는 시간도 물론 뜻 깊었지만, 하루 새에 다른 크루원들의 코드를 살펴보는 것은 정말 좋았다.

코멘트 달기

코멘트를 달까? 말까? 간질간질하다. 사실 PR 에서 코멘트를 다는 경험이 아직 그리 익숙치 않다. 나의 경우 고민하는 이유는 2가지였다.

  1. 내가 리뷰한 사항이 틀린 건 아닐까? 이 사람이 한 방식이 내가 말한 방식보다 사실 더 나은 건 아닐까? 그렇다면 나를 안좋게 평가하지 않을 까?

  2. 내가 생각하고 있는 이 부분은 굳이 코멘트가 달 필요가 있는 걸까?

  3. 은 그래서 리뷰 할 프로젝트 전체의 코드를 면밀히 살펴보고, 다른 리뷰가 있다면 그것도 참고하고, 지식적으로 헷갈리는 부분이 있다면 검색을 통해 충분한 검증이 필요하다고 생각한다. 그렇지 않고 리뷰를 다는 것은 존중이 부족한 것이라고 개인적으로 생각한다.

  4. 는 음.. 모르겠다. 정말 쓸모없는 부분이 아닌 이상, 리뷰를 받는 크루원도 긍정적으로 받아들이지 않을까? 라고 생각하려고 했다.

그래서 에라이 모르겠다 하고 달았다. 낯선 사람과 온/오프라인으로 소통하는 것에 그리 능통하지 않은 나에겐 약간의 용기 가 필요했다. 어쨌든, 한번 달고나니 그 이후에 다는 것은 괜찮았다. 칭찬, 감사의 인사, 제안 등 어떤 코멘트든 달고 나서 드는 기분은 다 좋았다.

처음 양방향 소통이 이루어진 크루원 분이었는데 정말 좋은 기분이었다. 아직 코멘트를 많이 달지 못했는 데, 더 달아볼 예정이다.

그리고 나도 코드 리뷰 요청하는 포스트를 올렸는 데 아직 리뷰가 없다.😢 다시 포스트를 올려봐야겠다..!!

몰입

몰입 은 명실상부 우테코의 핵심 가치인것 같다. 결론은 나는 이번 미션을 통해 몰입 을 경험한 것같다. 혼자서 고민하며, 그리고 다른 크루원들의 코드를 보며 리팩토링 할 때가 정말 즐거웠다.

최근 생산에 몰빵하여 단시간에 개발하는 프로젝트를 진행해서 그런지, 비교적 크지않은 프로그램 구석구석을 살펴보며 개선점을 찾는 행위가 너무 재밌었다.

그리고 이 회고, 회고에서도 몰입을 경험했다?! 사실 블로그에 쓰려고 쌓아둔 글이 정말 많다. 전부 50%, 80% 쓰다 말았다.

블로그 글을 쓸 때 자료 가 맞는 지, 더 좋은 게 있는 지 검색을 반복 하다가 내용이 산으로 가버리거나, 그 작업 자체가 버겁다 보니, 글을 마무리하는 것이 정말 쉽지 않았다. 그렇게 글을 쓰지 않다보니, 이제는 글을 시작하는 것 자체가 버겁게 느껴졌다. (그렇게 대단한 글을 쓰는 것도 아닌데, 전부 변명이긴 하다.)

그런 내가 글 하나를 이틀 내에 다 쓴건 이전에 블로그 글을 쓸 때부터 정말 이례적이다. 이유는 잘 모르겠지만, 커뮤니티가 우선 한몫했다. 많은 사람들이 공유하는 환경에 나도 포함되고 싶었던 것 같다.

그리고 다른 크루원들의 코드를 보며 배움을 얻는 과정이 너무 재밌어서 글로 옮기는 것에 위화가 없었다!

블로그 글을 빠른 시간내에, 즐겁게 자료를 찾아가며 마무리한 경험을 할 수 있게 해준 우테코 프리코스, 그리고 6기 크루원들 에게 정말 진심으로 감사하다.

profile
안녕하세요. 환영합니다. 프론트엔드 개발자 오현재입니다.

0개의 댓글