[TIL] 211008

Lee SyongΒ·2021λ…„ 10μ›” 8일
0

TIL

λͺ©λ‘ 보기
51/204
post-thumbnail

πŸ“ 였늘 ν•œ 것

  1. λ―Έλ‹ˆ κ²Œμž„ μ²˜μŒλΆ€ν„° λκΉŒμ§€ λ‹€μ‹œ κ΅¬ν˜„ν•΄λ³΄κΈ° (ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°, λͺ¨λ“ˆν™”)

πŸ“š 배운 것

1. λ―Έλ‹ˆ κ²Œμž„ λ‹€μ‹œ κ΅¬ν˜„

HTML/CSS/JavaScript λͺ¨λ‘ μ²˜μŒλΆ€ν„° λ‹€μ‹œ ν•œλ²ˆ κ΅¬ν˜„ν•΄λ΄„
JavaScriptλŠ” μ²˜μŒλΆ€ν„° ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ° 방식을 취함

1) 1μ°¨ μ™„μ„± μ½”λ“œ

λͺ¨λ“ˆν™” μ „

'use strict';

let carrotCount = 10;
let bugCount = 7;
const carrotSize = 120;
const bugSize = 80;
let gameDurationTime = 10;

const startBtn = document.querySelector('.setting-startBtn');
const gameTimer = document.querySelector('.setting-timer');
const gameLimit = document.querySelector('.setting-limit');

const gameField = document.querySelector('.game-field');
const popUp = document.querySelector('.game-popUp');
const replayBtn = document.querySelector('.popUp-replayBtn');
const popUpResult = document.querySelector('.popUp-result');

const bgSound = new Audio('sound/bg.mp3');
const alertSound = new Audio('sound/alert.wav');
const carrotSound = new Audio('sound/carrot_pull.mp3');
const bugSound = new Audio('sound/bug_pull.mp3');
const winSound = new Audio('sound/game_win.mp3');

let started = false;
let timer = undefined;
let limit = carrotCount;

startBtn.addEventListener('click', () => {
  if (!started) {
    startGame();
  } else {
    stopGame();
  }
});

function startGame () {
  started = true;
  initGame();
  changePlayIconToStopIcon();
}

function stopGame () {
  started = false;
  finishGame('replay ❓', alertSound);
}

gameField.addEventListener('click', (event) => {
  if (event.target.className === 'carrot') {
    event.target.remove();
    displayLimit(--limit);
    playSound(carrotSound);

    if (limit === 0) {
      finishGame('YOU WON πŸŽ‰', winSound);
    }

  } else if (event.target.className === 'bug') {
    finishGame('YOU LOST πŸ’©', bugSound);
  }
});

replayBtn.addEventListener('click', () => {
  started = true;
  initGame();
  showStartBtn();
  hidePopUp();
});

function initGame () {
  placeRandomly();
  startTimer();
  setLimitToCarrotCount();
  playSound(bgSound);
}

function finishGame (reultToShow, soundToPlay) {
  stopTimer();
  hideStartBtn();
  showPopUp(reultToShow);
  stopSound(bgSound);
  playSound(soundToPlay);
}

function placeRandomly () {
  gameField.innerHTML = '';

  makeImg('carrot', 'img/carrot.png', carrotSize, carrotCount);
  makeImg('bug', 'img/bug.png', bugSize, bugCount);
}

function makeImg(name, src, size, count) {
  for (let i = 0; i < count; i++) {
    const coordArray = makeRandomCoord(size);
    const img = document.createElement('img');
    img.setAttribute('src', src);
    img.setAttribute('class', name);

    img.style.left = coordArray[0] + 'px';
    img.style.top = coordArray[1] + 'px';

    img.style.width = size + 'px';

    gameField.appendChild(img);
  }
}

function makeRandomCoord (size) {
  const gameFieldRect = gameField.getBoundingClientRect();

  return [ gameFieldRect.left + Math.random() * ((gameFieldRect.width - size)),
           gameFieldRect.top + Math.random() * ((gameFieldRect.height - size)) ];
}

function startTimer () {
  let remainingTime = gameDurationTime;
  displayTime(remainingTime);
  timer = setInterval(() => {
    if (remainingTime === 0) {
      clearInterval(timer);
      showPopUp('REPLAY ❓');
      stopSound(bgSound);
      playSound(alertSound);
      return;
    }

    displayTime(--remainingTime);
  }, 1000);
}

function displayTime (time) {
  let minute = Math.floor(time / 60);
  let second = time % 60;

  gameTimer.innerHTML = `${minute}:${second}`;
}

function stopTimer () {
  clearInterval(timer);
}

function setLimitToCarrotCount () {
  gameLimit.innerHTML = carrotCount;
  limit = carrotCount;
}

function playSound (sound) {
  sound.currentTime = 0;
  sound.play();
}

function stopSound (sound) {
  sound.pause();
}

function changePlayIconToStopIcon () {
  const icon = document.querySelector('.setting-startBtn > i');
  icon.classList.add('fa-stop');
  icon.classList.remove('fa-play');
}

function showStartBtn () {
  startBtn.style.visibility = 'visible';
}

function hideStartBtn () {
  startBtn.style.visibility = 'hidden';
}

function showPopUp (result) {
  popUpResult.innerHTML = result;
  popUp.style.display = 'block';
}

function hidePopUp () {
  popUp.style.display = 'none';
}

function displayLimit (num) {
  gameLimit.innerHTML = num
}

2) μ΅œμ’… μ™„μ„± μ½”λ“œ πŸŽ‰

λͺ¨λ“ˆν™”+Ξ± μ™„λ£Œ

(1) main.js

'use strict';

import PopUp  from './popUp.js';
import { GameBuilder, Reason } from './game.js';
import * as Sound from './sound.js';

const gameFinishPopUp = new PopUp();
gameFinishPopUp.setReplayBtnClickListener(() => game.start());

const game = new GameBuilder()
  .withGameDurationTime(10)
  .withCarrotCount(10)
  .withBugCount(7)
  .build();

game.setStopGameListener((reason) => {
  let message;
  switch (reason) {
    case Reason.win:
      message = 'YOU WON πŸŽ‰';
      Sound.playWin();
      break;
    case Reason.lose:
      message = 'YOU LOST πŸ’©';
      Sound.playBug();
      break;
    case Reason.cancel:
      message = 'REPLAY ❓';
      Sound.playAlert();
      break;
  }
  gameFinishPopUp.show(message);
});

(2) popUp.js

'use strict';

export default class PopUp {
  constructor () {
    this.popUp = document.querySelector('.game-popUp');
    this.popUpResult = document.querySelector('.popUp-result');
    this.replayBtn = document.querySelector('.popUp-replayBtn');
    this.replayBtn.addEventListener('click', () => {
      this.onReplayBtnClick && this.onReplayBtnClick();
      this.hide();
    });
  }

  setReplayBtnClickListener(onReplayBtnClick) {
    this.onReplayBtnClick = onReplayBtnClick;
  }

  show (result) {
    this.popUpResult.innerHTML = result;
    this.popUp.style.display = 'block';
  }

  hide () {
    this.popUp.style.display = 'none';
  }
}

(3) field.js

'use strict';

import { ItemType } from './game.js';

const carrotSize = 120;
const bugSize = 80;

export default class Field {
  constructor (carrotCount, bugCount) {
    this.carrotCount = carrotCount;
    this.bugCount = bugCount;

    this.gameField = document.querySelector('.game-field');
    this.gameFieldRect = this.gameField.getBoundingClientRect();

    this.gameField.addEventListener('click', (event) => {
      if (event.target.className === ItemType.carrot) {
        this.onGameFieldCilck && this.onGameFieldCilck(ItemType.carrot);
      } else if (event.target.className === ItemType.bug) {
        <this.onGameFieldCilck && this.onGameFieldCilck(ItemType.bug)
      }
    });
  }

  setGameFieldClickListener (onGameFieldCilck) {
    this.onGameFieldCilck = onGameFieldCilck;
  }

  placeRandomly () {
    this.gameField.innerHTML = '';

    this._makeImg(ItemType.carrot, 'img/carrot.png', carrotSize, this.carrotCount);
    this._makeImg(ItemType.bug, 'img/bug.png', bugSize, this.bugCount);
  }

  _makeImg(name, src, size, count) {
    for (let i = 0; i < count; i++) {
      const coordArray = this._makeRandomCoord(size);
      const img = document.createElement('img');
      img.setAttribute('src', src);
      img.setAttribute('class', name);

      img.style.left = coordArray[0] + 'px';
      img.style.top = coordArray[1] + 'px';

      img.style.width = size + 'px';

      this.gameField.appendChild(img);
    }
  }

  _makeRandomCoord (size) {
    return [ this.gameFieldRect.left + Math.random() * ((this.gameFieldRect.width - size)),
             this.gameFieldRect.top + Math.random() * ((this.gameFieldRect.height - size)) ];
  }
}

(4) sound.js

'use strict';

const bgSound = new Audio('sound/bg.mp3');
const alertSound = new Audio('sound/alert.wav');
const carrotSound = new Audio('sound/carrot_pull.mp3');
const bugSound = new Audio('sound/bug_pull.mp3');
const winSound = new Audio('sound/game_win.mp3');

export function playBackground () {
  playSound(bgSound);
}

export function playAlert () {
  playSound(alertSound);
}

export function playCarrot () {
  playSound(carrotSound);
}

export function playBug () {
  playSound(bugSound);
}

export function playWin () {
  playSound(winSound);
}

export function stopBackground () {
  stopSound(bgSound);
}

function playSound (sound) {
  sound.currentTime = 0;
  sound.play();
}

function stopSound (sound) {
  sound.pause();
}

(5) game.js

'use strict';

import PopUp  from './popUp.js';
import Field from './field.js';
import * as Sound from './sound.js';

export class GameBuilder {
  withGameDurationTime (gameDurationTime) {
    this.gameDurationTime = gameDurationTime;
    return this;
  }

  withCarrotCount (carrotCount) {
    this.carrotCount = carrotCount;
    return this;
  }

  withBugCount (bugCount) {
    this.bugCount = bugCount;
    return this;
  }

  build () {
    return new Game(this.gameDurationTime, this.carrotCount, this.bugCount);
  }
}

export const Reason = Object.freeze({
  win: 'win',
  lose: 'lose',
  cancel: 'cancel'
});

const ItemType = Object.freeze({
  carrot: 'carrot',
  bug: 'bug'
});

class Game {
  constructor (gameDurationTime, carrotCount, bugCount) {
    this.gameDurationTime = gameDurationTime;
    this.carrotCount = carrotCount;
    this.bugCount = bugCount;

    this.started = false;
    this.timer = undefined;
    this.limit = this.carrotCount;

    this.gameTimer = document.querySelector('.setting-timer');
    this.gameLimit = document.querySelector('.setting-limit');
    this.startBtn = document.querySelector('.setting-startBtn');
    this.startBtn.addEventListener('click', () => {
      if (!this.started) {
        this.start();
      } else {
        this.stop(Reason.cancel);
      }
    });

    this.ClickGameField = new Field(this.carrotCount, this.bugCount);
    this.ClickGameField.setGameFieldClickListener((item) => this.onGameFieldClick(item));

    this.gameFinishPopUp = new PopUp();
  }

  setStopGameListener (onStopGame) {
    this.onStopGame = onStopGame;
  }

  start () {
    this.started = true;
    this.initGame();
    this.changePlayIconToStopIcon();
  }

  stop (reason) {
    this.started = false;
    this.stopTimer();
    this.hideStartBtn();
    Sound.stopBackground();
    this.onStopGame && this.onStopGame(reason);
  }

  initGame () {
    this.ClickGameField.placeRandomly();
    this.startTimer();
    this.setLimitToCarrotCount();
    Sound.playBackground();
    this.showStartBtn();
  }

  startTimer () {
    let remainingTime = this.gameDurationTime;
    this.displayTime(remainingTime);
    this.timer = setInterval(() => {
      if (remainingTime === 0) {
        clearInterval(this.timer);
        this.onStopGame && this.onStopGame(Reason.cancel);
        Sound.stopBackground();
        Sound.playAlert();
        return;
      }

      this.displayTime(--remainingTime);
    }, 1000);
  }

  displayTime (time) {
    let minute = Math.floor(time / 60);
    let second = time % 60;

    this.gameTimer.innerHTML = `${minute}:${second}`;
  }

  stopTimer () {
    clearInterval(this.timer);
  }

  setLimitToCarrotCount () {
    this.gameLimit.innerHTML = this.carrotCount;
    this.limit = this.carrotCount;
  }

  changePlayIconToStopIcon () {
    const icon = document.querySelector('.setting-startBtn > i');
    icon.classList.add('fa-stop');
    icon.classList.remove('fa-play');
  }

  showStartBtn () {
    this.startBtn.style.visibility = 'visible';
  }

  hideStartBtn () {
    this.startBtn.style.visibility = 'hidden';
  }

  displayLimit (num) {
    this.gameLimit.innerHTML = num
  }

  onGameFieldClick (item) {
    if (!this.started) {
      return;
    }
    if (item === ItemType.carrot) {
      event.target.remove();
      Sound.playCarrot();
      this.displayLimit(--this.limit);

      if (this.limit === 0) { // limit λŒ€μ‹ μ— gameLimit.innerHTML μ“°λ©΄ μ™œ μ•ˆ λ˜λŠ” 걸까? πŸ”₯πŸ”₯πŸ”₯
        this.stop(Reason.win);
      }

    } else if (item === ItemType.bug) {
      this.stop(Reason.lose);
    }
  }
}

3) 정리 및 질문 사항 πŸ™‹β€β™€οΈ

  • μ „μ—­ λ³€μˆ˜ μ„ μ–Έ μˆœμ„œ (κ°•μ˜ μ°Έκ³ )

    • 숫자λ₯Ό ν• λ‹Ήν•œ λ³€μˆ˜
    • HTMLμ—μ„œ λ°›μ•„μ˜¨ μš”μ†Œλ₯Ό ν• λ‹Ήν•œ λ³€μˆ˜
    • μ˜€λ””μ˜€ 객체λ₯Ό ν• λ‹Ήν•œ λ³€μˆ˜
    • κ²Œμž„ 진행 μƒνƒœλ₯Ό λ‚˜νƒ€λ‚΄λŠ” λ³€μˆ˜
  • gameField 클릭 이벀트 λ¦¬μŠ€λ„ˆ 콜백 ν•¨μˆ˜ 쀑 μ•„λž˜ μ½”λ“œμ˜ if μ‘°κ±΄μ‹μ—μ„œ limit λŒ€μ‹  gameLimit.innerHTML라고 μ“°λ©΄ μ˜λ„ν•œ λŒ€λ‘œ μž‘λ™ν•˜μ§€ μ•ŠλŠ”λ‹€. limitλ₯Ό μ¨μ„œ 해결은 ν–ˆμ§€λ§Œ, gameLimit.innerHTML은 μ™œ μ•ˆ λ˜λŠ” 걸까. πŸ™‹β€β™€οΈ

if (limit === 0) {
  stopTimer();
  hideStartBtn();
  showPopUp('YOU WON πŸŽ‰');
  stopSound(bgSound);
  playSound(winSound);
}
  • finish ν•¨μˆ˜μ˜ 인수λ₯Ό, κ²Œμž„ κ²°κ³Ό λ¬Έκ΅¬μ—μ„œ true/false둜 λ°”κΎΈκ³  game.js νŒŒμΌμ— setStopGameListenerλ₯Ό μž‘μ„±ν•˜λŠ” 것을 ν¬ν•¨ν•œ 일련의 과정듀을 λΉΌλ¨Ήμ—ˆλ‹€. μ΄λ ‡κ²Œ 해주지 μ•Šμ•„λ„ λͺ¨λ‘ 였λ₯˜ 없이 정상 μž‘λ™ν•˜κ³  λͺ¨λ“ˆν™”도 κ°€λŠ₯ν•œλ°, μ΄λ ‡κ²Œ ν•΄μ£ΌλŠ” 건 μœ μ§€Β·λ³΄μˆ˜λ₯Ό μ‰½κ²Œ ν•˜κΈ° μœ„ν•¨μΈκ°€. μ½”λ“œλŠ” 이해λ₯Ό ν•΄μ„œ μž‘μ„±μ€ 어렡지 μ•Šμ€λ° μ• μ΄ˆμ— μ΄λ ‡κ²Œ ν•΄μ•Όκ² λ‹€λŠ” 생각을 μ–΄λ–»κ²Œ ν•  수 μžˆμ„κΉŒ. πŸ™‹β€β™€οΈ

  • game.jsμ—μ„œ λΉŒλ” νŒ¨ν„΄ μž‘μ„± μ‹œ, ν•¨μˆ˜ μ•ˆμ— return thisλ₯Ό μ¨μ£ΌλŠ” 이유

    λ°°μ—΄μ˜ map()μ΄λ‚˜ reduce()λ₯Ό μ‚¬μš©ν•˜λ©΄ 배열이 returnλ˜λ―€λ‘œ, 여기에 λ©”μ„œλ“œλ₯Ό μ—°μ‡„μ μœΌλ‘œ 계속 μ‚¬μš©ν•  수 μžˆλ‹€. 이와 λ™μΌν•œ 효과λ₯Ό λ‚΄κΈ° μœ„ν•¨μ΄λ‹€. μ•„λž˜ μ½”λ“œμ²˜λŸΌ μž‘μ„±ν•  수 μžˆλ‹€.

// main.js δΈ­
const game = new GameBuilder()
  .withGameDurationTime(10)
  .withCarrotCount(10)
  .withBugCount(7)
  .build();
  • μ–΄λ–€ νŒŒμΌμ—μ„œ export defaultκ°€ μ—†μœΌλ©΄, λ‹€λ₯Έ νŒŒμΌμ—μ„œ import 뒀에 {}λ₯Ό λ°˜λ“œμ‹œ μ¨μ€˜μ•Ό ν•œλ‹€.

  • gamefield의 μ†Œκ΄€(이미지 랜덀 배치, 이미지 클릭)이 μ•„λ‹ˆκΈ° λ•Œλ¬Έμ— Field 클래슀의 λ©”μ„œλ“œλ‘œ 적어주지 μ•Šμ„ ν•¨μˆ˜λ“€(displayLimit, finishGame, playCarrot)κΉŒμ§€ λ‹€λ₯Έ js νŒŒμΌμ„ 거쳐 μ²˜λ¦¬ν•˜κΈ° μœ„ν•΄ 적어쀀 것

    μ–΄λ–€ κ²½μš°μ— μ•„λž˜μ™€ 같은 μ‹μ˜ μ½”λ“œλ₯Ό 써 λ„£λŠ”μ§€ ν—·κ°ˆλ ΈλŠ”λ°, μœ„ λ‚΄μš©μ²˜λŸΌ μ΄ν•΄ν–ˆλ‹€. 근데 μΌλ°˜ν™”ν•΄μ„œ κΈ€λ‘œ μ„€λͺ…ν•˜κΈ°κ°€ μ–΄λ ΅λ‹€

this.onGameFieldCilck && this.onGameFieldCilck('carrot');
  • popUp이 뜨면 λ‹Ήκ·Όκ³Ό λ²Œλ ˆκ°€ ν΄λ¦­λ˜μ§€ μ•Šλ„λ‘ μ½”λ“œλ₯Ό μˆ˜μ •ν–ˆλ‹€.
// field.js μˆ˜μ •
this.gameField.addEventListener('click', (event) => {
  if (event.target.className === ItemType.carrot) {
    this.onGameFieldCilck && this.onGameFieldCilck(ItemType.carrot);
  } else if (event.target.className === ItemType.bug) {
    this.onGameFieldCilck && this.onGameFieldCilck(ItemType.bug)
  }
});
// game.js μˆ˜μ •
onGameFieldClick (item) {
  if (!this.started) { // startedκ°€ false이면, ν΄λ¦­λ˜μ§€ μ•Šλ„λ‘
    return;
  <}
  if (item === ItemType.carrot) {
    event.target.remove();
    Sound.playCarrot();
    this.displayLimit(--this.limit);

    if (this.limit === 0) {
      this.stop(Reason.win);
    }

  } else if (item === ItemType.bug) {
    this.stop(Reason.lose);
  }
}

4) 배운 점 및 λŠλ‚€ 점

  • μ΄μ œκΉŒμ§€λŠ” μ–΄λ–»κ²Œλ“  였λ₯˜ 없이 μž‘λ™ν•˜κ²Œ λ§Œλ“œλŠ” κ²ƒμ—λ§Œ 신경을 μ¨μ™”λŠ”λ°, 그게 μ „λΆ€κ°€ μ•„λ‹˜μ„ 느꼈던 μ‹€μŠ΅μ΄μ—ˆλ‹€. κ·Έλ™μ•ˆ μ ˆμ°¨ν˜• ν”„λ‘œκ·Έλž˜λ°μ— μ΅μˆ™ν•΄μ Έ κ°„λ‹¨ν•˜κ³  μ‰½κ²Œ μ“Έ 수 μžˆλŠ” μ½”λ“œλ₯Ό 자꾸 λŒμ•„ λŒμ•„ μ–΄λ ΅κ²Œ ν‘œν˜„ν•˜λŠ” κ±° κ°™λ‹€.

    사싀 ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ€ κ±±μ •ν–ˆλ˜ 것보닀 이해가 잘 λΌμ„œ 잠깐 μ‹ λ‚¬μ—ˆλ‹€. 그런데 생각도 λͺ»ν–ˆλ˜, κ·Έ μ „κΉŒμ§„ 이런 게 μ‘΄μž¬ν•˜λŠ”μ§€λ„ λͺ°λžλ˜ 'λͺ¨λ“ˆν™”'λΌλŠ” λ§ˆμ§€λ§‰ λ‹¨κ³„μ—μ„œ κ½€λ‚˜ μ• λ₯Ό λ¨Ήμ—ˆλ‹€. 처음으둜 ν•˜λ£¨ μ˜¨μ’…μΌμ„ 써도 κΉ”λ”ν•˜κ²Œ 이해가 μ•ˆ λΌμ„œ 잠깐 μ’Œμ ˆν•˜κΈ°λ„ ν–ˆλ‹€. κ·Έλž˜λ„ μ–΄λ–»κ²Œλ“  κ²°κ΅­μ—” μ΄ν•΄ν–ˆλ‹€.

    λ‹€λ§Œ, 이 쀑 λͺ‡ ν”„λ‘œλ‚˜ μ˜¨μ „νžˆ λ‚΄ 것이 λ˜μ–΄ μžˆμ„μ§€ λͺ¨λ₯΄κ² λ‹€. μœ„μ— 써놓은 κ²ƒμ²˜λŸΌ 이 μ½”λ“œ μžμ²΄λŠ” μ΄ν•΄ν•˜μ§€λ§Œ, μ—¬κΈ°μ„œ μ–΄λ–»κ²Œ 이런 μ‹μœΌλ‘œ 생각할 수 μžˆμ„κΉŒ, 싢은 뢀뢄이 μžˆλ‹€. λ‹€λ₯Έ μ˜ˆμ œμ—μ„œ 같은 상황을 λ§Œλ‚¬μ„ λ•Œ λ‚΄κ°€ κ³Όμ—° 슀슀둜 μ €κ±Έ μƒκ°ν•΄μ„œ μ μš©ν•  수 μžˆμ„κΉŒ 싢기도 ν•˜λ‹€.

  • κ·Έλž˜λ„ 이번 μ‹€μŠ΅ μ „κΉŒμ§€λŠ” λ­”κ°€ μ•ŒκΈ΄ μ•„λŠ”λ° 이게 μ œλŒ€λ‘œ μ•„λŠ” 건지 μ–΄λ–€ 건지 μ’€μ²˜λŸΌ λ§‰μ—°ν•œ 감이 있던 ν•¨μˆ˜λž€ 아이와 μ‘°κΈˆμ΄λ‚˜λ§ˆ μΉœν•΄μ§„ λ“―ν•œ λŠλ‚Œμ΄ λ“œλŠ” 게 이번 μ‹€μŠ΅μ˜ κ°€μž₯ 큰 μˆ˜ν™• κ°™λ‹€.

    λͺ¨λ“ˆν™”+Ξ± 같은 κ²½μš°λ„ μ–΄μ œκΉŒμ§€λ§Œ 해도 νŒŒνŽΈν™”λœ 지식 λ•Œλ¬Έμ— λΆ„λͺ…νžˆ 배우긴 ν–ˆμ§€λ§Œ λ‚΄ 것이 μ•„λ‹Œ λŠλ‚Œμ΄ μžˆμ—ˆλŠ”λ°, 였늘 λ‹€μ‹œ μ²˜μŒλΆ€ν„° λκΉŒμ§€ 슀슀둜 κ΅¬ν˜„ν•΄ λ³΄λ©΄μ„œ λΉ„λ‘œμ†Œ λ‚΄κ°€ μ–΄λ–€ 뢀뢄을 μ–΄λ–»κ²Œ μ΄ν•΄ν•˜κ³  μžˆλŠ”μ§€ λ‚˜λ¦„λŒ€λ‘œ μ •λ¦¬ν•˜λŠ” μ‹œκ°„μ„ κ°€μ§ˆ 수 μžˆμ—ˆλ‹€.

  • 이둜써 λΈŒλΌμš°μ € 101 κ°•μ˜λ„ 완강을 ν–ˆλ‹€. μ•žμœΌλ‘œ ν•΄μ•Ό ν•  것듀을 적어봀닀. 이렇닀할 μ‹€μŠ΅μ΄ μ—†μ–΄μ„œ 이둠 곡뢀와 ν•¨κ»˜ λ―Έλ‹ˆ ν”„λ‘œμ νŠΈλ₯Ό κ²Έν•΄μ•Ό ν•  κ±° κ°™λ‹€.

    λΈŒλΌμš°μ € 101 κ°•μ˜ 볡슡 / ν”„λ‘œκ·Έλž˜λ¨ΈμŠ€ λ¬Έν’€ & 자료ꡬ쑰 μ•Œκ³ λ¦¬μ¦˜ κ°•μ˜ μˆ˜κ°• / git κ°•μ˜ μˆ˜κ°• / ν”„λ‘œκ·Έλž˜λ° μ±… 읽기 / λ¦¬μ•‘νŠΈ κ°œλ… κ°•μ˜ μˆ˜κ°•


✨ 내일 ν•  것

  1. λΈŒλΌμš°μ € 101 κ°•μ˜ 볡슡

  2. ν”„λ‘œκ·Έλž˜λ¨ΈμŠ€ λ¬Έν’€

  3. 자료ꡬ쑰 μ•Œκ³ λ¦¬μ¦˜ κ°•μ˜ μ•Œμ•„λ³΄κΈ°

profile
λŠ₯λ™μ μœΌλ‘œ μ‚΄μž, ν–‰λ³΅ν•˜κ²ŒπŸ˜

0개의 λŒ“κΈ€