어제에 이어서, 과제를 계속 진행했다.

 

사실상, 목표로 했던 부분은 다 완성을 했다고 볼 수 있다 !

 

 

시작 화면은 어제와 같지만 !

 

 

1. 등산 시작을 통해 첫 스테이지로 진입 할 수 있다.

보이는 기능들은 전부 구현이 된 상태이다.

 

1. 등반하기  (평타) 를 통해, 스테이지를 클리어한 모습

 

 

스테이지를 클리어 할 시, 랜덤보상이 3개 주어진다 !

 

// 랜덤 뽑기 보상 ~~ !
function RandomReward(player, monster, stage) {
  
  // Math.random()*100 // 80이상~ 60이상~ 40이상~ 20이상~ 0이상~
  let Reward = Math.random()*100+1
  // 일부 보상에 0.8 ~ 2.0 배율 적용
  let RewardMulty = ((Math.random()*1.2)+0.8).toFixed(1);
         
      // 보상 종류 1 : 회복
  if ( Reward >= 80) {
    let playerHpRestoration = Math.floor(RewardMulty*(stage*12+70))
    player.hp += playerHpRestoration
    console.log(chalk.yellowBright(`산바람이 시원하다. 체력이 [${playerHpRestoration}] 회복되었다 !`));
    
    //보상 종류 2 : 공격력
  } else if ( Reward >= 60) {
    let playerPowerInc = Math.floor(RewardMulty*(stage*2+5))
    player.power += playerPowerInc
    console.log(chalk.yellowBright(`근육이 늘었다. 등산력이 [${playerPowerInc}] 증가했다 !`));

    //보상 종류 3 : 공격 배율
  } else if ( Reward >= 40) {
    let playerPowerMulty = Math.floor(RewardMulty*(stage*2+7))
    player.powerMore += playerPowerMulty
    console.log(chalk.yellowBright(`산에 버려진 무공비법을 찾았다. 최대배율이 [${playerPowerMulty}] 증가했다 !`));

        //보상 종류 4 : 필살 충전
  } else if ( Reward >= 20) {
    if( player.specialMovePoint < 3) {
      player.specialMovePoint++
      console.log(chalk.yellowBright(`필살 ! 암벽등반 ! 충전이 1회 증가했다 ! 현재 충전[${player.specialMovePoint}]`));
    } else { console.log(chalk.yellowBright(`필살 ! 암벽등반 충전 ! 하지만, 이미 최대 충전 상태다...  `)); }

    //보상 종류 5 : 꽝으로 마무리?-> 축지법 성공률로 대체했다.
  } else {
    player.skillChance += Math.floor(4*RewardMulty)
    console.log(chalk.yellowBright(`보법이 달라졌다. 축지법 성공률이 ${Math.floor(4*RewardMulty)}% 증가했다 !`));
  }
};

 

랜덤 뽑기 함수가 현재 구현한 함수중에 길이가 가장 길다...

 

 

심지어 호출부, 인터페이스 부분은 따로있음

 

 

2. 축지법 스킬은 기본 20%확률 밖에 안되기 때문에

보상을 통해 확률이 늘어난게 아니라면 개 손해인 선택지이다.

 

1. 오늘의 첫번째 문제 발생

 

전투 진행상황을 알려주는 초록색 로그 부분이 길어지면, 상태창이 밀려 올라가게 된다.

현재 상태를 알기가 어렵게 되고,

스크롤을 올려서 위를 보면 

뭔가 의도되지 않는 로그가 보이는 등, 문제가 발생한다.

콘솔창을 최대로 키운상태로 진행하면 덜 할 수 있지만, 밀려올라가는건 마찬가지 !

 

해결 방법

로그를 출력하는 battle 함수에서

로그 출력 전 while 문을 달아서 로그가 너무 많을 경우,

0번 index 를 날려버리는 shift() 메서드를 반복하게 했다.

 

 

로그가 일정량 쌓이면, 먼저 들어갔던 로그부터 제거된다.

해결!

 

 

2.  두번째 문제 !

 

이건 간단한 문제로 빠르게 해결했다.

오늘의 목표였던 

 

충전식 필살 스킬 !

충전을 모아서 사용해야 하는데, 이 충전 변수를 어디어 두어야 할지 모르겠다.

함수 안에서 선언하면 어느 함수에서? 호출도 힘들텐데?

전역에 둘까..

전역에 두자니, 허허 벌판에서 혼자 떨고있는 변수가 안쓰럽기도 하고

"코딩 그렇게 하는거 아닌데"  하는 목소리가 계속 들려온다.

 

생각보다 답은 간단했다.

어차피 여기저기 불려다녀야하는 player의 형성자에  넣어주면 되는것이었다.

 

위에서 본 랜덤보상에서 해당 포인트를 얻을 수 있고, 

3포인트를 모아서 사용하면 

 

잘 작동하는 모습이다..

사실 이 장면에 오늘의 가장 큰 문제가 된 사항이 포함되어있다.

 

그건 바로..

3.  필살 이후 나오는 문구에 딜레이를 줘 볼까?

5~10회나 공격하는 기술인데, 텍스트가 한번에 출력되는 것보다

두두두두두두 하고 딜레이에 맞춰서 드드등장하면?

완전 개 간지  ! 

당장 해보자.

 

강의 시간에 배운 setTimeout 이라던가..  async  promise await ?? 같은걸 쓰면

간단한 문제라고 생각했다...

 

시간이 이렇게 되기 전까지는 !

그런 생각으로인해,  TIL을 이 시간에 작성하게 되었다

 

 

 setTimeout 을 아무리 써봐도,

promise ...await 를 이쁘게 넣어봐도

코드가 작동이 되지 않는다.

error 가 발생하는것도 아니고, 해당 함수에서 delay 부분 코드만 실행이 안되고 넘어가버린다.

 

이건 분명 기다려주지 않는것이다... !!!

기다려준다고 promise 했자나요!!

 

 

이번에도 답은 간단했다.......

해당 함수에 async 달고~ setTimeout 세팅해준다고 끝나는 것이 아니라,

 

해당 함수를 호출하는 친구또한 await를 달아줘야 한다.

즉, 필살기 끝나기 전까지 비-동기 진행하지마 ! 를 입구부터 걸어놓고 와야한다.

playerSpecialMove 내부 코드도 동기 처리가 되어야겠지만,

playerSpecialMove 밖에서는 안에서 동기처리가 되든말든 알 바가 아니기 때문에, 진행해버리기 때문이다.

허무해 !!  내 시간 !!

 

 

잘 작동한다 !

'내일배움캠프' 카테고리의 다른 글

[내일배움캠프] 24.08.27 TIL  (1) 2024.08.27
[내일배움캠프] 24.08.23 TIL  (0) 2024.08.23
[내일배움캠프] 24.08.21 TIL  (0) 2024.08.21
[내일배움캠프] 24.08.20 TIL  (0) 2024.08.20
[내일배움캠프] 24.08.19 TIL  (0) 2024.08.19

 

 

 

텍스트로 이루어진 간단한(?) 로그라이크 게임을 만드는 과제가 주어졌다.

 

 

완성 예시 )

이런걸..?

 

 

이제 막 문법을 배웠는데 게임을 만들라구요?!

 

 

 

????

 

 

물론, 스켈레톤 코드가 제공된다고 해도 분명 쉬운 과제는 아니다.

실행이 가능할 정도의 뼈대만 구성이 되어 있었고, 나머지는 전부 직접 만들어야한다.

 

 

 

기본적으로, 위와 같은 템플릿이 주어지지만,

적정선 내에서는 일부 기능을 포기한다거나, 새로운 기능을 추가할 수 있다.

 

계획 !

계획만 그럴싸 하면 어떻게든 진행될 것이다..

 

1. 컨셉

우선 컨셉을 잡아보기로 했다.  (코딩에 바로 들어가기가 두렵다)

원본 컨셉   <===>    나의 컨셉

게임                             등산

stage                          현재 높이

monster                      등산로

공격, 공격력          등반, 등산력...?!

 

나름 그럴싸한, 식상하지 않은 컨셉이라고 생각한다.

 

 

 

2. 목표

본인은 현재 도전 ! 코스로 올려치기가 되어있는 상태로,

일반 코스와 비교해서 추가적인 기능을 구현해야 하는 상태이다.

 

 

 cmd로 실행하면, 왜 인지 폰트가 상당히 낡아지는

 

오늘은 간단하게(?) 텍스트를 정리하고,

가장 핵심 목표인 추가 스킬에 대해 구상을 해 보았다.

추가로 오류가 날 확률이 적다고 판단되어지는 부분은 우선적으로 구현을 해 보았다.

 

1. 등반하기 -> 일반 공격이다.   간단하게 공격력 만큼의 피해를 주는것에 더해서..

공격력에 배율을 곱해서 최대 데미지 개념을 만들려고 했는데 생각보다 쉽지 않았다 !!

잘 모르겠지만, 일단 의도대로 작동하고 있다 !

 

2. 축지법 !   ->  회피와 공격을 동시에

성공시, 체력소모 없이 공격이 가능한 스킬이다. 실패한다면 피해를 못주고 맞기만 한다.

일단은 고정확률인 60% (59%인가....?으음...) 로 마무리할 예정이기 때문에

대충 구현을 마친 상태다.

 

 

3. 필살 ! 암벽등반 ! -> 필살 스킬로, 아직은 미구현이다.

아무튼 강한 피해를 입히는데, 중요한건 충전식으로 충전스택이 2일 경우에만 사용이

가능하게 해볼 예정이다. 충전 스택은 스테이지 클리어 보상으로 랜덤하게 얻을수 있고,

최대 2까지만 쌓을 수 있고, 사용시 2를 소모한다.

 

4. 지름길 개척 :  도망가기 스킬이다.

마찬가지 아직은 미구현으로,

낮은 확률로 성공하여 보상을 모두 얻을지,

그럴싸한 확률로 성공하지만, 클리어 보상이 적어지게 할지 아직 정하진 않았다.

구현..가능 하겠지?

 

 

아직, 초기 단계 이지만, 

 

코딩

좀.. 재미있을지도...

 

 

 

'내일배움캠프' 카테고리의 다른 글

[내일배움캠프] 24.08.23 TIL  (0) 2024.08.23
[내일배움캠프] 24.08.22 TIL  (0) 2024.08.22
[내일배움캠프] 24.08.20 TIL  (0) 2024.08.20
[내일배움캠프] 24.08.19 TIL  (0) 2024.08.19
[내일배움캠프] 24.08.16 TIL  (0) 2024.08.16

 

 

5주차의 내용은 크게 

 

1. DOM

 

2. 클래스

 

3. 클로저 

 

로 나누어진다.

 

 

 

1. DOM = document object modeling

브라우저에 기본적으로 내장되어있는 API 중 하나이다.

JS 는 기본적으로 브라우저 환경에서 작동하기 위해 만들어 졌으므로, 

HTML 과의 상호작용이 당연히 가능해야한다.

 

Dom Tree 는   html구조를 JS가 이해 할 수 있도록 내용을 해석한 것이다.

어떤 형태로?  Object 형태로 !

 

1-1 DOM 의 node 

 

 

1-2 DOM 요소의 속성과 메서드 구분 

document는 대상 객체 ( document 는 항상 dom tree 최상단의 노드 !)

getElementByid() 는.. 인자와 같은 id 를 가진 요소 하나를 반환하라는 메서드 

innerHTML 은 그 속성  !

 

 

 

2. Class 

 

ES6 에서 도입된 문법으로, 

다른 언어에서 많이 사용되던 클래스 기반의 프로그래밍 개발 기법 의 유행(?)으로 인해

자바스크립트에서도 억클( 억지 class 문법...) 을 하려다 보니 니즈를 반영하여 만들었다고 하더라 ~

 

기본적으로 알아둬야 할 사항으로,

Class 와 Instance 가 있다.

 

그럼 JS 에서는 뭘 만드는 설계도 인가?

바로, 객체를 만드는 설계도 이다.

그리고 설계한 대로 만들어진 객체는 instance 가 되겠다.

 

동작을 찍어내는 function, 객체를 찍어내는 class

묘하게 비슷한 감이...?

 

class Person {
	// constructor는 이름을 변경할 수 없어요.
  constructor(name, age) {
		// 이름(name)과 나이(age)가 없으면 사람이 아니죠?
		// new라는 키워드를 이용해서 인스턴스를 만들 때, 기본적으로
		// 넣어야 하는 값들을 의미해요! :)
		// 여기서 말하는 this는 만들어질 인스턴스를 의미한다고 생각해주세요!
    this.name = name;
    this.age = age;
  }

	// 다양한 메소드를 아래와 같이 정의할 수 있어요.
	// 여기서 this.name으로 내부 값을 접근해야 함을 잊지 마세요! :)
  sayHello() {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
  }
}

const person1 = new Person("Alice", 30);
const person2 = new Person("Bob", 25);

// 만든 객체를 토대로 메서드 호출해보기
person1.sayHello(); // 출력: "Hello, my name is Alice and I am 30 years old."
person2.sayHello(); // 출력: "Hello, my name is Bob and I am 25 years old."

 

 

person 이라는 class는,  name 과 age 만 받는다면 새로운 person 객체를 마구마구 생성할 수 있는 설계도이다.

 

추가로, person class는 함수 또한 내장이 가능하고, 생성된 객체도 해당 함수를 가져간다.

 

또...중간에 못보던 녀석이 있다 !

Constructor() ; 

 

 

 

 

Getter 와 Setter 

get 을 통해 class 속성에 접근하여, 원하는 값을 반환하게 할 수 있다 !

set 을 통해 class 속성에 접근하여, 원하는 값으로 설정할 수 있다 !

 

 

상 속  Inheritance

 

객체를 복사하는 설계도는 class 

class를 복사하는 설계도는 상속 이라고 보면 될 듯 하다.

Animal class 로 부터 상속받는 Dog class 를 생성

상속 과정에서 필요한 부분을 입맛에 맞게 수정할 수 있다는게 핵심이다.

 

 

Static Method

 

 

3. 클로저

dom 과 class 가 디저트 였다면,

클로저가 이번 챕터의 메인 보스 이다.

 

 

 

클로저를 생각할 때 기억해야할 것  !!

중첩 함수 , 종료된 변수 , 여전히 참조

 

콜스택 적 으로 표현한다면,

이미읽어서 실행이 종료된 함수의 스코프는 외부 스코프로 나가면서 지워지게 된다.

원래대로 라면, 더 이상 해당 스코프의 내용은 참조할 수가 없게된다.

 

그러나, 클로저(중첩함수) 가 존재하는 함수라면, 실행 컨텍스트에서 제거되지만,

해당 함수의 렉시컬 환경까지 소멸하는 것이 아니다.

 

왜?

더 이상 쓰일 일이 없는 데이터를 제거하는 프로세스인

가비지 컬렉터 << 이 유능한 친구가 참조할 데이터가 남아있기 때문에 렉시컬 환경은 남겨두는 것이다.

 

 

왜 클로저를 써야할까? 

 

즉, 클로저로 인해 일어나는 현상에서 이득이 많기 때문에 쓴다는 말인데,

 

가장큰 장점은,

함수 내부로 변수를 은닉하여 의도되지 않은 변경으로 부터의 보호가 가능

이라는 것이다 !

 

( 아무튼 의도적으로 만들어진 기능은 아닌듯 함 )

 

 

this의 대상이 중구난방 이었던과 비슷하게,

 

closer 또한 closer 로 구성된 코드인지, 아닌지를 구분해야 하는 능력을 키워야 할 것 같다 !

 

'내일배움캠프' 카테고리의 다른 글

[내일배움캠프] 24.08.22 TIL  (0) 2024.08.22
[내일배움캠프] 24.08.21 TIL  (0) 2024.08.21
[내일배움캠프] 24.08.19 TIL  (0) 2024.08.19
[내일배움캠프] 24.08.16 TIL  (0) 2024.08.16
[내일배움캠프] 24.08.14 TIL  (0) 2024.08.14

 

 

 

주요 내용은 다음과 같다.

 

  1. 자바스크립트의 콜백함수의 개념과 예시에 대해 학습
  2. 어떤 항목에서 콜백함수를 전달받은 함수에게 제어권이 이양되는지를 이해하고 그 예시를 테스트
  3. 비동기적인 코드 작성에서 발생할 수 있는 콜백지옥을 예방하고 비동기제어를 할 수 있는 Promise, async/await 등의 기술을 이해하고 활용하는 능력 배양

 

 

 

1. 콜백 함수 

콜백 함수는 이전 부터 계속 등장했던 함수이다.

함수 또는 메서드의 인자 부분에 함수가 들어가는것이 가능한데,  ( 일급 객체 )

그렇게 인자로써 매개변수로 넘겨지는 함수를 콜백 함수라고 한다.

 

이번 주차에서는 추가로 제어권에 관한 학습을 진행한다.

 

제어권 ??

 

쉽게 말하면,

cbFunc(); 일반 함수실행의 경우, 사용자가 해당 함수를 실행하도록 만든것이고

setInterval(cbFunc, 300);  의 경우, setInterval 메서드가 (cbFunc, 300) 를 실행하는 것이다.

 

 

콜백함수도 함수다

var obj = {
	vals: [1, 2, 3],
	logValues: function(v, i) {
		console.log(this, v, i);
	}
};

//method로써 호출
obj.logValues(1, 2);

//callback => obj를 this로 하는 메서드를 그대로 전달한게 아니에요
//단지, obj.logValues가 가리키는 함수만 전달한거에요(obj 객체와는 연관이 없습니다)
[4, 5, 6].forEach(obj.logValues);

 

위 예시는 콜백함수의 함정에 가까운 부분으로, 

언뜻 보면 ~forEach(obj.logvalues)   obj 객체를 대상으로하는 logvalues 메서드 이기 때문에 

obj 에 this가 바인딩 될것 같지만,

사실 저 부분은, obj의 logvalues가 인자를 인질로 가지고 있다.. 정도 밖에 안된다

어디까지나, 인자이기 때문

 

obj 가 아닌 global 이 우수수

 

 

 

 

콜백 지옥

 

 

 

대체 어느 부분이 어느 함수의 내용인지 알기가 어렵게 되어버린다.

코드를 짜다 보니 저렇게 될 수도 있다는 것인데,

수정과 유지보수 또한 hell 이기 때문에 최대한 지양해야한다.

 

 

동기와 비동기

 

 

동기란, 1-2-3-4-5 작업이 있을 때, 1번 작업이 완료되지 않는다면, 2-3-4-5 작업이 영원히 실행조차 안되는걸 말한다.

 

비 동기란, 동기의 반대말 !!

1번 작업이 완료되지 않더라도 상황에 따라 다음 순서의 작업이 실행되고, 완료 될 수 있다는 것을 말한다.

 

 

예시를 들자면...........으음

 

사격장에서 사격 훈련을 한다고 했을때,

 

동기 => 

1번 사수 사격- > 완료 -> 2번 사수 사격 -> 완료 -> 3번 사수 사격 -> 완료

 

비 동기 =>

준비된 사수로부터 사격 개시 => 완료 순서도 제각각

 

라고 할 수 있겠다.

 

 

콜백 지옥 해결법? 

setTimeout(
  function (name) {
    var coffeeList = name;
    console.log(coffeeList);

    setTimeout(
      function (name) {
        coffeeList += ", " + name;
        console.log(coffeeList);

        setTimeout(
          function (name) {
            coffeeList += ", " + name;
            console.log(coffeeList);

            setTimeout(
              function (name) {
                coffeeList += ", " + name;
                console.log(coffeeList);
              },
              500,
              "카페라떼"
            );
          },
          500,
          "카페모카"
        );
      },
      500,
      "아메리카노"
    );
  },
  500,
  "에스프레소"
);

 

보기만 해도 현기증이 나는 평범한 콜백 지옥이다.

 

강의에서는 

 

1.  기명함수로 변환  

2. 비 동기 작업의 동기적 표현

 2-1 ) promise

 2-2 ) Generator

 2-3 ) Promise + Async/await 

 

을 소개해주었다.

 

여기서 좀 더 중요한 녀석은 비 동기 작업을 동기적으로 표현한다는 내용인데, 

 

왜 굳이 비 동기 작업을 동기적으로 바꾸려고 할까?  라고 생각이 든다면, 

 

일에는 순서가 있는 법이야 를  떠올려 보자.

물론 가독성과 유지보수의 문제도 있겠다만,

 

우리는 음식을 주문받는 일과, 요리가 만들어지는 과정, 요리가 서빙되는 과정이

동시에 시작될 수 없다는 것을 잘 알고있다 .

 

주문을 받고-> 요리가 만들어지고-> 서빙이 되어야 하기 때문이다....

???

 

말하고 보니까 굉장히 이상한 느낌이 든다..

 

애초에 한번에 한 가지의 동작만 수행하도록 되어있는 동기식 언어인 JS에서, 

효율을 위해 Queue , call stack 등을 이용해 비 동기식으로 처리될 수 있게끔 만들었고,

그렇게 만들어진 비 동기 방식을 우리는 다시 동기적으로 표현할 수 있어야 한다.

 

 

 

 

 

'내일배움캠프' 카테고리의 다른 글

[내일배움캠프] 24.08.21 TIL  (0) 2024.08.21
[내일배움캠프] 24.08.20 TIL  (0) 2024.08.20
[내일배움캠프] 24.08.16 TIL  (0) 2024.08.16
[내일배움캠프] 24.08.14 TIL  (0) 2024.08.14
[내일배움캠프] 24.08.13 TIL  (0) 2024.08.13

 

 

 

어제 못다한 3주차의 this 에 대해 알아보자 . 

 

 

 

this는 꽤 악명이 높기 때문에 시작부터 긴장이...

 

 

 

 

 

기억해야할 것은 

 

1. 전역 공간에서의 this 는 객체를 가리킨다.

2. 메서드로서 호출할 때 는 해당 메서드를 호출한 객체를 지정한다.

 

3. 몇가지 방법으로 this 를 임의로 지정 객체에 binding 할 수 있다.

 

아무도 안 쓸거 같은 방법이라고 생각이 되어지는데..

 

 

 

화살표 함수는 정말 중요한 부분이라고 생각한다.

일반 함수처럼 func() 로 호출하여도  func 가 () => {}  화살표 함수로 선언이 되어있다면,

상위 객체를 정상적으로 지정하게 된다.

 

 

 

 

콜백 함수도 우선 함수이기 때문에, 기본적으로 this 는 전역객체를 가리킨다.

 

 

 

 

우리는 명시적 this 바인딩을 통해, this 가 특정한 객체를 바라보게 하는 법을 알아햐 한다 !

 

 

 

call 과 apply 메서드는 거의 비슷한 메서드 이다.

기본적으로 실행 명령이고,  

func.call( { x : 1 } , 4, 5, 6 } ;    

의 형태로, 첫 번째 인자로 binding 대상을 지정하게 된다

 

obj.method.apply({ a: 4 }, [5, 6]); .  

apply 도 동일하지만, 나머지 인자를 배열로 넘겨주게 된다.

 

 

 

 

대놓고 이름부터 bind 인 메서드도 있다.

이미 fucn 함수에 특정한 값을 bind 한것 자체를 변수명로 만드는 것이다.

중요한건,  실행하는 메서드가 아니라 bind 값을 저장해놓는 용도이기에 위에 call apply 와 차별점이 있다.

미리미리 필요한 정보를 바인드 해놓고 대시키실 수가 있는것이다.

 

 

 

결국 화살표 함수를 쓰면 상위 객체를 가리키기 때문에 

화살표 함수를 쓰면 거의 대부분의 상황에서는 의도에 맞게 this 를 사용할 수 있다.

 

 

 

쓰다가 한 번 본문이 날아가 버렸다 !!!

 

내용이 뭔가 허 해진것 같은 느낌같은 느낌이 들지만..

(왜 인지 ...페이지가 다운된 뒤에 최근 작성한 글 이어쓰기가 안되었다.)

 

 

어차피 한번 강의에 모든 this 를 통달하는것을 불가능 하기 때문에,

이론 위주로 학습을 하였고 다소 암기가 필요한 부분은 본문에 정리를 해 둔.. (것이 날라감)

필요할 때 3주차 부분을 다시 들어야 겠다는 생각이 든다. 

 

this 가 계속해서 날 괴롭힐테니 말이다 

'내일배움캠프' 카테고리의 다른 글

[내일배움캠프] 24.08.20 TIL  (0) 2024.08.20
[내일배움캠프] 24.08.19 TIL  (0) 2024.08.19
[내일배움캠프] 24.08.14 TIL  (0) 2024.08.14
[내일배움캠프] 24.08.13 TIL  (0) 2024.08.13
[내일배움캠프] 24.08.12 TIL  (0) 2024.08.12

 

3주차 강의에서는

데이터 타입과 실행 컨텍스트에 대해서 학습한다

 

 

데이터 타입은 크게 

기본형과 참조형으로 나뉘고 

참조형은 Object (객체) 로서 다뤄진다는 것이 강조된다.

 

이어서, 메모리와 데이터에 관계에 대해서 알아 봤는데,

충격적인 내용이 꽤 있다.

 

var y1 = [1,2,3]
var y2 = [1,2,3]
console.log(y1===y2)   // false 를 반환 이전에 알고있던 부분

 

y1 과 y2는 동일한 [1,2,3] 배열의 데이터를 가지고 있지만,

선언하는 과정에서 각각 데이터의 주소를 새로 할당 받았기 때문에

1,2,3 데이터는 같을지라도 주소를 가르키는 주소값이 달라

y1 === y2  ->   false 를 반환한다.

 

 

 

var s1 = [1,2,3]
var s2 = s1

 

위의 경우도 마찬가지로,

s2 = [1,2,3] 의 데이터를 갖지만 

1,2,3 을 가르키는 새로운 주소를 할당받은게 아니기 때문에 

개별적인 메모리가 아닌 s1 과 같은 메모리를 공유하게 된다.. ㄷㄷ

따라서,

console.log(s1===s2) // true 반환

s1 === s2 는 1,2,3 을 가르키는 주소값 자체가 동일하기 때문에 true 를 반환한다.

 

여기 까지는 그럴수 있다 하고 이해하는 내용이지만..

 

s2.push(4)

 

이 상태로, s2 에 4를 push 하면

console.log(s2)  // [1,2,3,4]

당연하게도  s2= [1,2,3,4]  가 된다.

 

문제는,

console.log(s1)  // [1,2,3,4]
 

굉장히 무섭게도, s1 은 push가 없었음에도, s2 와 동일한 [1,2,3,4] 를 갖게 된다.

push를 받은 시점에서 데이터가 달라졌음에도 (가리키는 데이터가 추가)

s1과 s2 는 데이터를 가르키는 주소값이 같기 때문에

s1도 영향을 받는 결과가 나오는 것이다.

 

상기의 이유로,

참조형 데이터를 갖는 변수는 

var arr2 = arr1   과 같은 방법으로는 데이터를 '복사' 해올 수 없다.

 

따라서, 참조형 데이터로서 같은 값을 온전히 복사 하기 위해선

 

var y1 = [1,2,3]
var y2 = [1,2,3]

 

와 같은 형태의 하드 코딩이 필요하다.

 

 

다른 방법은 없을까? 

1. 얕은 복사

 

//이런 패턴은 어떨까요?
var copyObject = function (target) {
	var result = {};

	// for ~ in 구문을 이용하여, 객체의 모든 프로퍼티에 접근할 수 있습니다.
	// 하드코딩을 하지 않아도 괜찮아요.
	// 이 copyObject로 복사를 한 다음, 복사를 완료한 객체의 프로퍼티를 변경하면
	// 되겠죠!?
	for (var prop in target) {
		result[prop] = target[prop];
	}
	return result;
}

 

강의에서는 얕은 복사라는 방법으로,

함수와 for in 문을 통해, 참조형 데이터가 갖는 값을 하나하나 복사 하는 방법을 소개했다.

 

 

음....

그런데 왜 이름이 얕은 복사일까?

??? : 네, 이 얕은 복사 패턴도 여전히 문제가 있습니다...

 

 

얕은 복사의 방법으로는 중첩된 객체를 완벽히 복사 할 수 없다.

 

var user = {
	name: 'wonjang',
	urls: {
		portfolio: 'http://github.com/abc',
		blog: 'http://blog.com',
		facebook: 'http://facebook.com/abc',
	}
   };

위의 경우,

user 객체 내에 urls 객체가 또 있기 때문에

결국

user2.urls === user.urls    ==> true가 되어버리는 문제가 생기는 것이다.

 

 

2. 깊은 복사

 

해결책인 깊은 복사는, 

참조형 데이터를 복사할 때마다 그 안의 내용 또한 복사해서 

주소값을 기어코 만들어버리고야 마는 그런 내용이다.

 

지금 시점에서 깊은 복사를 깊이 이해하기에는 무리라는 판단이 들어,

이론만 짚고 넘어가기로 했다.

 

왜냐하면 다음에 나올 내용이 굉장히 중요하다고 생각되기 때문이다.

 

 

 

 

실행 컨텍스트

 

실행 컨텍스트는, 자바 스크립트가 실행되는 순서에 관련한 매커니즘으로,

 

실행할 코드에 제공할 환경 정보들을 모아놓은 객체  이다.

 

 

 

 

JS... 이거 맞아?

 

화나는 포인트중 하나

호이스팅(hosting) 의 경우,

마치 자연원리를 이론으로 설명하듯 한다는것이다.

js가 그렇게 만들어졌기에  그렇게 작동하는 것일텐데 말이다...

 

var a = 1;
function outer() {
	function inner() {
		console.log(a); //undefined
		var a = 3;
	}
	inner(); // ---- 2번
	console.log(a);
}
outer(); // ---- 3번
console.log(a);

 

콜스택, 호이스팅, 스코프 체인의 동작 방식을 이해하기 위해

저 몇줄 되지 않는 코드를 반나절 가까이 들여다보고 있었다.

 

실행 컨텍스트에 대해서 모른다면,

저 console.log(a) 들이 무슨 값을 표시할지 전혀 예측할 수 없다. 전혀 !!!

 

var a = 1;
    function outer() {
        function inner() { // --- 1번
            console.log(111, a);  // a = undefined
            var a =3 ;
            console.log(222, a);   // a = 3
       a }
        inner(); // ---- 2번
        console.log(333, a);  //  = 1
    }
    outer(); // ---- 3번
    console.log(444, a);   // a = 1

 

전역부터 시작 !
var a  호이스팅
function outer 호이스팅
a = 1   *스코프 체이닝의 근거
outer(); 스택됨

전역 -> outer 영역 이동
function inner 호이스팅
inner(); 스택됨

outer -> inner 영역 이동
var a  // 호이스팅 => 전역에서 가져온 a = 1 데이터가 있지만, var a 에 의해서 초기화됨 (스코프 체이닝 불가 = 섀도잉 된다)
111 => undifined //  위의 이유로 undifined 출력
a=3
222 => 3  // 바로 위에서 할당

inner -> outer 영역 이동
333 => 1 // 진입 당시 전역에서 var a =1 을  들고왔었기 때문에 a= 1 이라는 정보가 남아있음 (스코프 체이닝)

outer -> 전역 영역 이동
444 => 1 // var a= 1 이 할당된 곳

 

내 이해를 위처럼 정리 해 보았다.

 

튜터님도 제대로 이해한 것이 맞다고 하셔서 안심이 됐다 !

 

나름대로의 뿌듯함

이게... 코딩?

 

 

팩트는, Tihs 가 남아있다는거임

 

This 에 대해서는 다음 시간에 알아보도록 하자 !

'내일배움캠프' 카테고리의 다른 글

[내일배움캠프] 24.08.19 TIL  (0) 2024.08.19
[내일배움캠프] 24.08.16 TIL  (0) 2024.08.16
[내일배움캠프] 24.08.13 TIL  (0) 2024.08.13
[내일배움캠프] 24.08.12 TIL  (0) 2024.08.12
[내일배움캠프] 24.08.09 TIL  (0) 2024.08.09

 

 

오늘, 강의의 2주차 학습에서 알아볼 내용은 다음과 같다.

 

  • 각종 ES6 문법 
  • 일급 객체로서의 함수
  • Map과 Set
  • 숙 제

 

각 항목에서 짚고 넘어갈 부분만 다시 되새겨 보자.

 

 

 

- 화살표 함수

화살표 함수는 저번 1주차 강의에서 한번 봤던 내용으로,

ES6 에서 추가된 새로운 함수 선언 문법이다.

 

// (2) arrow function
 function add () {
    일반 함수
 }
 

 var add = () => {
    화살표 함수
 }

 

기존에는 작성 방법만 다른 줄 알았으나,

많은 차이가 있었다.

 

  1. 익명 함수로만 만들 수 있다.
  2. 생성자로 사용할 수 없다.
  3. 스스로의  this, argument를 가지지 않는다
  4. 함수가 정의된 스코프에 존재하는 this 를 가리킨다.
  5. 생성될 때 this가 결정된다.
  6. 함수 사용방법이나 호출 방법에 의해 this 가 바뀌지는 않는다.
  7. return 하지 않아도 값이 변환된다.

대부분은 당장 이해하기 힘든 내용이 많다.

 

체크해야할 부분은

화살표 함수는 선언할 때 this가 정해지기 때문에 

함수가 어떻게 사용되고 호출되던간에 이미 this는 고정되어 있다.

반면에 어떻게, 어디서 선언되는가는 매우 중요하다고 할 수 있다.

 

 

 

- 전개 구문

let arr = [1, 2, 3];
console.log(arr);       // [1, 2, 3]
console.log(...arr);    // 1 2 3
console.log([...arr])   // [1, 2, 3]

...배열 또는 ...객체 를 이용하여 

값을 전개 할 수 있다.

 

 

 

- 템플릿 리터럴

간간히 봤던것으로, 

 ``  백틱과   ${} 달러  로 표기하는 문자열 표현식 이다.

// 템플릿 리터럴 (Template Literal)
// `` 백 틱
const testValue = "안녕하세요 !"
console.log (`Hello World ${testValue}`);

console.log(`
    Hello
        My name is Javascript !!!
            Nice to meet you !!
    `)

 

백틱 내에서 텍스트를 쓰다가도 ${} 를 통해 함수나 변수를 호출 할 수 있는데다가 

띄어 쓰기, 줄 바꿈 까지 전부 그대로 표현 할 수 있는 강한 파워(?) 를 가진 표현식이다.

 

 

 

그리고, 본격적으로 머리가 아파지기 시작하는 

 

일급 객체로서의 함수 

 

function callFunction (func) {
 
    func();  //매개변수로 받은 변수가 함수가 되었다.
}

const sayHello = function() {  //인자가 된 함수 정의
    console.log('Hello');  
}

callFunction(sayHello);  // 함수를 호출하는데, 인자도 함수

절반 가까운 내용을 위 코드로 정리할 수 있다.

 

callFunction(sayHello);  

에서 callFunction 함수가 어떻게 정의되어있는지와,

그 매개변수로 들어갈 인자 sayHello 가 어떻게 정의되어있는지를 동시에 찾게된다 (수정)

---> JS 에서는 병렬처리란 없기 때문에 동시에 라는 말은 틀리다 ! 

 

 

function callFunction (func) {
   
    func();
}

callFunction 의 정의를 찾고 보니, 매개 변수로 받은 함수를 그냥 실행하도록 정의되어있다.

callFunction(sayHello);

이로인해  위는 사실상 sayHello(); 와 같은말이 된다.

 

동시에

const sayHello = function() {  //인자가 되어버린 함수 정의
    console.log('Hello');
}

sayHello 는  cosole.log 'Hello' 를 실행하는 함수이기 때문에,

 

const sayHello = function() {  
    console.log('Hello');
}

sayHello();

결과적으로는 위와 같다고 할 수 있다.

 

 

중요한것은 !!!

위에 내용은

인자에도, 매개변수에도, 함수내부에서도

 

 

어디에도 함수가 사용될 수 있다는걸 말해주고있다.

const sayHello = function() {
    console.log('Hello');
}

심지어 변수에도 함수를 할당할 수 있다...

변수에 함수를 할당했다고 해야하는건지,

그냥 함수를 정의해야 했다고 하는건지 애매하지만, 둘다 맞는 말일듯??

24.08.14 추가

-  학습을 일반적인 함수 선언과 변수를 통한 함수 선언은 차이가 있다는것을 배웠다.

-  일반 함수 선언은 호이스팅이 적용되어, 우선 함수선언을 읽어들인다.

-  변수를 통한 함수 선언도 호이스팅은 되지만, 해당 구문에 도달하기 전에 변수가 읽어지는것을 막는다.   ( TDZ 에 의해) 

 

 

-숙 제 -

https://school.programmers.co.kr/learn/courses/30/lessons/12915

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

 

열심히 배웠으니까

배운 내용으로 풀 수 있는 문제를 제시해주겠지?

 

하지만 늘 그렇지 않다.

알고리즘을 위해 또 학습을 해야한다.

 

 

이 문제를 풀기 위해서 따로 필요한 것이 있다.

 

1. 특정 문자를 대체하고 반환하는 .replace 함수 

혹은,

2. 특정 위치의 문자열만 참고하여 sort 할 수 있는... 있을지 없을지 모르는 미지의 방법 

 

1번을 택한다면,

1-1. n 번째 문자를 밸류 맨 앞에다 임시로 복사한 뒤 sort 하고 다시 제거한 후 정렬

1-2.  n번째 까지 문자를 제거한 뒤 sort 하고 다시 복구하는 방법..?

 

function solution(strings, n) {
    var answer = [];
// 1. 문자열 가장 앞 글자를 붙인 배열 만들기
for(var i = 0; i < strings.length ; i++) {
    strings[i] = strings[i][n] + strings[i]
}

// 2. 해당 배열을 사전순으로 정렬 sort
strings.sort();

// 3. 앞글자를 제거 후 리턴
for( var j = 0 ; j < strings.length; j++) {
    strings[j] = strings[j].replace(strings[j][0],"");
    answer.push(strings[j])
}
    
    return answer;
}

위 코드는 1-1 에 해당하는 방법이다.  가장 정석적인 방법

replace 사용법만 익힌다면 나머지는 어렵지 않게 작성할 수 있다.

 

 

 

1-2 도 과연 가능할까?...

function solution(strings, n) {
    var result = [];
    var answer = [];
    var box = [];
    for(var i = 0; i < strings.length ; i++) {
        answer.push(strings[i].slice(n))
        box.push(strings[i].substr(0,n))
    }
    answer.sort();
    for(var j = 0; j< strings.length ; j++ ) {
        result.push(box[j]+answer[j])
    }
    return result;
}

 

될까 싶어 만들어 보다가 문제가 발생했다...

위의 방법대로라면,

answer 가 sort 되어 오름차 정렬될때, 잘려나간 나머지인 box의 내용도 동일한 위치로 정렬을 할 수 있어야한다.

아쉽게도 , 그 방법을 결국 찾아내지 못했기 때문에 폐기 하기로 했다.

 

다른 사람의 풀이

function solution(strings, n) {
    var answer = [];
    return strings.sort((s1, s2) => s1[n] === s2[n] ? s1.localeCompare(s2) : s1[n].localeCompare(s2[n]));
}

 

그 미지의 방법이다.

 

해석을 해 보면,

strings.sort 를 실행해 정렬을 하는데,

n 번째 문자가 동일한 경우는, 문자열 전체 sort 를 적용하고,

그외 다른 경우는 n번째 문자를 기준으로 sort 를 실행하는 코드이다.

.sort 에 기본 내장된 함수인 .sort(( a ,b) => ( a - b)) 코드에 더해

 

a.localeCompare(b) 같은 경우는 

a 가 b 보다 빠른 문자열 일 경우 -1

a 와 b 가 같은 순서일 경우 0

a 가 b 보다 느린 문자열 일 경우 1 을 반환한다.

해당 함수로 대상문자 a와 b 의 순서를 비교하여 .sort를 실행하게 된다.

 

 

 

 

'내일배움캠프' 카테고리의 다른 글

[내일배움캠프] 24.08.16 TIL  (0) 2024.08.16
[내일배움캠프] 24.08.14 TIL  (0) 2024.08.14
[내일배움캠프] 24.08.12 TIL  (0) 2024.08.12
[내일배움캠프] 24.08.09 TIL  (0) 2024.08.09
[내일배움캠프] 24.08.08 TIL  (0) 2024.08.08

 

 

 

 

내일배움캠프 2주차 의 시작으로

자바 스크립트 기본 문법에 해당하는 강의를 지급받았다.

 

 

어떻게 웹페이지 제작 과제가 끝난 이후에 

'기본문법' 강의를 지급하는것이지? 

 

라고 생각할 뻔 했으나,

웹페이지 제작 과제는 어디까지나

사전캠프 중,  [웹개발 종합반] 강의를 들은 사람들에 한해서만 진행된 것이기 때문에

사실상 본격적인 내일배움캠프의 학습은 이제부터 시작이라고 할 수 있다.

 

 

 

주요 학습 내용은 

자바 스크립트 이론과 기초 문법이다. 

 

대부분은 사전 캠프를 진행하며

생활코딩 << 에서 학습한 내용과, 웹개발 종합반에서 이미 선행 학습한 부분이다.

 

복습하는 마음으로 학습을 진행 했는데, 

 

역시 처음 배울 때에 비해서, 훨씬 이해가 잘 되고

 

아아... 이것이 ,  "복습 " 인가?

하는 생각이 들었다.

 

 

그러나 순조로웠던 학습에 문제가 발생했다. 

숙제 중, 반복문, 조건문 연습하기 라는 부제로 올라온 내용은

킹양 갓하기

음양 더하기로,  N일차 사전캠프에서 선행학습한 적이 있는 알고리즘 퀴즈 !

 

 

자신만만하게 

코드 초기화 버튼을 냅다 누르고 문제를 읽기 시작했는데 

 

...어? 

가 되어버린 것이다.

 

TIL 까지 작성하며 되새겼던 문제를 불과 2주 만에 잊어버리는 문제가 발생 !!

 

결국 , 강의에 포함되어있는 문제 풀이 영상을 보고 풀었고,

이후 과거의 내가 작성한 코드를 확인 해 보았다.

 

function solution(absolutes, signs) {
    var nn = [];
    var answer = 0;
    for ( var i = 0; i < absolutes.length; i++) {
     if ( signs[i] == false) {
         nn.push(absolutes[i] * -1)
     } else nn.push(absolutes[i] * 1)
    } 
    for ( var i = 0; i < nn.length ; i++) {
        answer += nn[i] 
    }
    return answer;

과거의 풀이 초안은 지금 보더라도 굉장히 지저분하고

불필요한 요소가 많이 들어 있었다. 하지만, 중요한것은 어떻게든 풀었다는 것.

 

 

function solution(absolutes, signs) {
    var answer = 0;
    for(var i = 0; i < signs.length ; i++) {
    answer = signs[i] === true ? answer+absolutes[i] : answer-absolutes[i]
    }
    return answer;
}

 

막상 풀이를 보고나니,

기억이 난다... 라기 보다는,  막혔던 혈이 뚫린듯한 느낌으로  빠르게 풀어낼 수 있었다.

 

이것또한 복습의 효과라고 생각할 수 있지만, 이래서는 안된다고 생각한다.

 

앞으로는 하루를 마무리 하는과정에서

이미 풀었던 지난 알고리즘 문제를 한번더 풀이하는 시간을 가져야겠다.

 

'내일배움캠프' 카테고리의 다른 글

[내일배움캠프] 24.08.14 TIL  (0) 2024.08.14
[내일배움캠프] 24.08.13 TIL  (0) 2024.08.13
[내일배움캠프] 24.08.09 TIL  (0) 2024.08.09
[내일배움캠프] 24.08.08 TIL  (0) 2024.08.08
[내일배움캠프] 24.08.07 TIL  (0) 2024.08.07

 

 

캠프 시작과 동시에 진행됐던 첫 번째 프로젝트가 끝났다.

 

최종 제출한 페이지의 모습

 

 

 

 

 

 

초안과 비교하면 어마어마한 차이를 볼 수 있다.

 

내가 쓰고, 가져오고, 붙인 코드뭉치들

 

중구 난방의 함수명과 변수명,

가독성이 떨어지는 코드 배치,

나름대로 험난했던 과정을 표현한 것일지도 모른다..

 

 

 

프로젝트를 마치며 ...

 

캠프 시작과 동시에 진행된 프로젝트이니 만큼,

마음의 준비가 안된 상태인데다, 코딩에 관한 지식도 너무 부족했다.

 

하지만, 시간이 지나, 뼈대가 잡히자

의외로 순조롭게 프로젝트가 진행되었다.

 

구현 가능할까 걱정이었던 crud 부분도

Update 부분을 제외하고는 잘 작동했다.

 

 

 

Delete  기능을 어떻게 작동시킬까 하는 부분에서 

버튼 자체에 ID값을 넣을 수 있다는 부분은 나름대로 충격이었다.

 

 

프로젝트 중간중간 안풀리는 부분에 있어서는 GPT 를 활용하기도 했는데,

 

기술이 많이 발전했다는걸 뼈부터 느낄 수 있었다.

 

 GPT를 원하는 코드를 짜주는 기계가 아닌,

모르는 부분을 대답해주는 선생으로 생각한다면 계속 활용하는것도 나쁘지 않다고 생각한다.

 

팀 프로젝트 4일차 !

 

내일이 발표이기 때문에 사실상 작업 마지막 날이다.

오늘까지 작업물을 완성하고

시연 영상 제작과 발표자 선정도 필요하다.

(나만 아니면 돼)

 

 

 

1. 수정과 삭제 기능

 

어제 보류했던 기능을 마저 구현해보기로 했다.

나의 역할 중 가장 중요한 부분이기 때문에 집중해 보기로 했다.

 

작동이 잘 안된다거나

데이터 베이스가 달라서 이해가 안되는 등

우여곡절이 많았으나

 

수소문 끝에 통해 방명록 등록 / 삭제 기능을 찾아내었다.

 

삭제 기능 구현에 대한 실마리를 찾은것이다 !

 

 

 

삭제 버튼으로 해당 메세지를 삭제하는기능까지 정상적으로 작동했다.

 

 

중요한 부분을 짚고 넘어가면

                <button class="deleteButton" onclick="deleteEntry('${doc.id}')">삭제</button>

 

데이터를 불러와 메시지를 띄우는 시점에 버튼을 추가하며

해당 버튼은 온클릭= 함수(doc.id) =   파이어 베이스 문서 부분의 id 를 지정한다

 

window.deleteEntry = async (entryId) => {
    if (confirm("이 항목을 삭제하시겠습니까?")) {
        await deleteGuestBookEntry(entryId);
        displayGuestBookEntries(); // 방명록 목록 갱신
    }
};

 

클릭할 경우 deleteEntry함수로 confirm 확인을 한 후에

확인인 경우 deleteGuestBookEntry 함수를 적용한다... 

 

async function deleteGuestBookEntry(entryId) {
    try {
        const docRef = doc(db, "guestBook", entryId);
        await deleteDoc(docRef);
        console.log(`Guest book entry with ID ${entryId} has been deleted.`);
    } catch (error) {
        console.error("Error deleting guest book entry:", error);
    }
}

 

어....... 아직 이해하기가 힘든 코드지만,

대략적으로  deleteDoc을 적용해  db 에서 -> guestBook 에 있는 -> entryId 를 가진 데이터를 삭제한다...

 

 

삭제가 잘 작동하는 모습

 

이제 작업물에 적당히 적용하기만 하면 된다.

 

잘 작동하는 코드를 가져다 쓰는건 크게 어렵지 않다.

 

위의 방명록 코드에서 삭제 부분만 따온뒤 

버튼 부분에 같은 기능을 하는 함수를 달아주면 된다!

(함수명 변수명은 ... 따로 건드리지 않았다)

 

    // 멤버 삭제 함수
    async function deleteGuestBookEntry(entryId) {
      const docRef = doc(db, "ctrl_cv", entryId);
      await deleteDoc(docRef);

    }

    // 멤버 삭제 클릭
    window.deleteEntry = async (entryId) => {
      if (confirm("이 멤버를 삭제하시겠습니까?")) {
        await deleteGuestBookEntry(entryId);
        window.location.reload();
      }
    };

불필요한 부분을 적당히 날려주고

                <button onclick="deleteEntry('${doc.id}')">삭제</button>

 

생성되는 버튼에 함수를 달아주면 된다.

 

잘 작동한다 !

 

 

아무튼 삭제된 모습

 

 

 

남는 시간을 활용해

암호를 맞춰야 멤버 생성이 가능하도록 기능을 추가해 보았다.

 

      const makemempa = document.getElementById("makemempass").value

 

makemempa 변수에 

        <input type="password" id="makemempass" placeholder="암호 입력">

 

암호 입력에 적은 숫자를 대입한다. ( 좀 더 직접적으로는 안되는걸까?)

 

if (makemempa !== '1234') {
        alert("올바른 암호를 입력해 주세요.");
   return;

멤버 생성 할 때, 비밀번호 (1234) 를 체크 한 후  아닌 경우  return; 으로 멤버 생성을 중단해주면 완성?

 

 

아주 낮은 수준이겠지만 잘 작동한다 .

1234 이외를 쓴 경우

 

 

1234 를 입력한 경우

 

 

1234를 입력한 경우에는 기존처럼 멤버 생성이 잘 작동하는걸 확인할 수 있었다.

 

 

수정기능도 비슷한 방향으로 구현이 가능할 것 같으나, 

시간 관계상 제외하는 방향으로 가기로 결정했다.

 

다행히 기능구현 부분은 성공적으로 마무리했다. 

 

 

 

최종 완성본 ! 

 

+ Recent posts