본문 바로가기
TIL & 일지

TIL, 06.13

by Hyeon_E 2023. 6. 13.

- 깃허브 fork 이용해 보기

항해99 과제를 내기 위해 팀장님에 repository를 fork해보았다

git도 아직 잘 다루지 못하는데 fork는 아예 처음해보아서 구글링을 하여 fork에 대해 알아보고 시도를 해보았다 

 

fork는 다른 사람의 레포지토리를 그대로 복사해 내 소유의 레포지토리로 가져오는 기능이다

fork한 repository는 내것이기 때문에 내 마음대로 수정할 수 있고 원본 repository, 즉 다른 사람의 repository에는 영향을 주지 않는다. 이러면 원본 소스에 대한 무분별한 수정을 막을 수 있다

 

fork 기능을 사용하려면 fork를 한후 fork된 repository를 clone 한다

그리고 코드를 수정하여 push를 해 소스코드를 업로드하고 원본 저장소에 pull request 한다

그 후 원본 repository의 주인이 merge를 하면 된다

 

실제로 기능을 사용해 보려고 fork를 하고 pull request난 후 문제가 생겼다

처음에 clone하였을때는 원본 repository와 내용이 같기 때문에 pull request하는데 문제가 없었는데 다른 팀원이 pull request를 하고 merge가 된 후 나는 fork된 repository와 연결되어 있기 때문에 변경된 내용을 pull로 받아올 수 없었다

 

이때 remote로 저장소를 하나 더 만들어 이 문제를 해결할 수 있었다

다른사람의 repository fork 한 경우 내 repository가 origin이 되기 때문에 upstream으로 remote 저장소를 하나 더 만드는 것이다

 

git remote add upstream 원본repository주소

 

이렇게 터미널에 입력하면 새로운 저장소를 만들 수 있다

 

 

그 후 원본 repository내용을 pull하기 위해서는 외부 원본 저장소의 최신 내용을 가져온 다음

git fetch upstream

 

merge할 브랜치로 변경, 원본 저장소의 변경사항을 로컬 브랜치에 merge 한다

git checkout main
git merge upstream/main

 

그리고 내 repository에 merge 한 내용을 반영해 준다

 

- 항해99 JS 문법 종합반 3주차 듣기

이번 문법 종합반 3주차의 중요한 내용은

  • 깊은복사와 얕은 복사
  • 실행 컨텍스트

깊은 복사와 얕은 복사

먼저 깊은 복사와 얕은 복사에 대해 제대로 알기 위해서는 변수 선언과 데이터 할당에 대한 개념부터 제대로 이해해야 한다

먼저 데이터를 선언하여 값을 넣을때 메모리에 이름과 값 한번에 입력하는 것이 아니라 값은 다른 곳에 저장되어있고 그 주소를 가져와 입력한다는 점이 중요하다!!

 

JS에서 '기본형 데이터는 불변'하고  '참조형 데이터는 불변하지 않다'고 하는데 이유는 데이터의 영역에 저장된 값은 여전히 계속 불변하지만, 참조형데이터를 위한 별도 영역은 얼마든지 변경이 가능하기 때문이다

이것 때문에 참조형 데이터를 불변하지 않다고 하는것 이다

 

문제는 참조형 데이터를 변경하거나 복사하는데 일어난다

기본형은 서로 다른 데이터 영역의 주소를 바라보고 있기 때문에 영향이 없지만 참조형은 똑같은 주소를 바라보고 있기 때문에 복사한 다른 참조형도 값이 변경된다

 

이렇게 복사한  참조형으로 인해 데이터 값이 변경되는 막기위해 객체 자체를 변경하는 방법이 있다

참조형데이터가 복사되고 난후 변경하여 값이 변경될때는 참조형 데이터 자체를 변경할 경우가 아니라 그 내부의 프로퍼티를 변경할 때 성립한것이기 때문에 객체 자체를 복사하여 값을 변경하는 것이다

 

하지만 이때에도 문제가 있는데 프로퍼티가 엄청나게 많다면 그 많은 프로퍼티 다 입력해야 한다는 점이다

그래서 다른 방법으로 '얕은 복사'가 있다

 

var copyObject = function (target) {
	var result = {};

	for (var prop in target) {
		result[prop] = target[prop];
	}
	return result;
}

 

위의 예시는 for in 구문을 이용하여, 객체의 모든 프로퍼티에 접근하여 복사를 한것이다

이렇게 복사를 한다음 복사를 완료한 객체의 프로퍼티를 변경하면 된다

 

하지만 이렇게 얕은 복사에도 여전히 문제는 존재한다

왜냐하면 중첩된 객체에 대해서는 완벽한 복사를 할 수 없기 때문이다

얕은 복사는 바로 아래 단계의 값만 복사하기 때문에 중첩된 객체가 있다면 중첩된 객체는 값이 달라진다

 

그래서 이 문제를 해결하기 위해 깊은 복사가 나왔다!!

깊은 복사는 내부의 모든 값들을 하나하나 다 찾아서 모두 복사하는 방법이다

즉 깊은 복사까지 할줄 알아야 근본적인 문제를 해결할 수 있다

 

var copyObjectDeep = function(target) {
	var result = {};
	if (typeof target === 'object' && target !== null) {
		for (var prop in target) {
			result[prop] = copyObjectDeep(target[prop]);
            // 객체이고 null이 아닐경우 자기자신을 다시 실행
		}
	} else {
		result = target;
	}
	return result;
}

 

기본형 데이터는 그대로 복사하고 참조형데이터는 다시 그 내부의 프로퍼티를 복사하는 재귀적 수행을 하여 완벽한 다른 객체를 반환할 수 있다. 이말은 곧 내가 원하는 내가 그토록 바라는 깊은 복사를 완벽하게 구현한것이다!!

 

실행컨텍스트

실행컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다

JS는 어떤 실행 컨텍스트가 활성화되는 시점에  다음과 같은 일을 한다

  1. 선언된 변수를 위로 끌어올린다 = 호이스팅(hoisting)
  2. 외부 환경 정보를 구성한다
  3. this 값을 설정한다

콜스텍은 실행할 코드에 제공하는 환경 정보들을 모아놓은 객체이다

동일 환경에 있는 코드를 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고 이것을 위에서 설명드린 ‘스택’의 한 종류인 콜스택에 쌓아올린다

가장 위에 쌓여있는 컨텍스트와 관련된 코드를 실행하는 방법으로 코드의 환경 및 순서를 보장할 수 있다

 

컨텍스트의 구성으로는 전역공간, eval()함수, 함수가 있는데 금 중 우리가 흔히 실행 컨텍스트를 구성하는 방법에는 함수가 있다

실행 컨텍스트의 순서로는  실행 컨텍스트가 생성되는(또는 활성화되는) 시점이 콜 스택의 맨 위에 쌓이는(노출되는) 순간을 의미한다. 곧, 현재 실행할 코드에 해당 실행 컨텍스트가 관여하게 되는 시점을 의미한다고 생각하면 된다

 

실행 컨텍스트 객체의 담기는 정보로는 VE, LE, ThisBinding이 있는데 VE와 LE는에 담기는 항목은 동일하나 LE는 변경사항을 실시간으로 반영한다는 것이 다르다

VE와 LE가 담기는 항목으로는 environmentRecord(=record)와 outerEnvironmentReference(=outer)가 있다

 

LE의 관련된 코드의 environmentRecord(=record)는 식별자 정보들을 저장(수집)하는데 이때 수집 대상 정보로 함수에 지정된 매개변수 식별자, 함수자체, var로 선언된 변수 식별자 등이 있다

 

변수수집을 모두 마쳤더라도 아직 실행 컨텍스트가 관여할 코드는 실행 전의 상태를 호이스팅이라고 한다

모드 실행 전 이미 모든 변수정보를 알고 있는 것을 말한다

 

호이스팅은 매개변수 및 변수는 선언부를 호이스팅하고 함수 선언은 전체를 호이스팅한다

하지만 여기서 변수가 발생하는데 함수 선언문은 함수 전체가 위로 끌어올려지는 반면 함수 표현식은 변수 부분만 위로 끌어올려진다

 

//원래코드
console.log(sum(1, 2));
console.log(multiply(3, 4));
function sum (a, b) { // 함수 선언문 sum
	return a + b;
}
var multiply = function (a, b) { // 함수 표현식 multiply
	return a + b;
}

//호이스팅
function sum (a, b) { // 함수 선언문 sum
	return a + b;
}
var multiply; 
console.log(sum(1, 2));
console.log(multiply(3, 4));
multiply = function (a, b) {
	return a + b;
};

 

함수 선언문으로 함수를 작성하게 되면 만약 잘못 코드를  작성할 경우에 함수 전체가 위로 끌어올라가 전체적으로 잘못된 영향을 미친다

그래서 협업을 많이 하고, 복잡한 코드 등에 코드 협업에는 함수 표현식을 활용하는 습관을 들여야 한다!!

 

마지막으로 실행컨텍스트 관점에서의 스코프를 알아보면 일단 스코프는 식별자에 대한 유효범위를 의미한다

스코프 체인은 식별자의 유효범위를 안에서부터 바깥으로 차례로 검색해 나가는 것을 말한다

LE의 구성요소 중 outer의 역할은 스코프 체인이 가능토록 하는것이다(외부 환경의 참조정보)

이때 가장 중요한 점은 outer은 오직 자신이 선언된 시점에 LE를 참고하고 있으므로 가장 가까운 요소부터 차례대로 접근이 가능하다 그래서 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에게만 접근이 가능하다

 

- 숫자야구 프로그램 만들기

간단한 숫자야구 프로그램을 JS로 만들어 보았다

0부터 9 사이의 서로 다른 숫자 3개를 무작위로 뽑으면 사용자는 정답을 맞추면 된다

숫자의 값과 위치가 모두 일치하면 S, 숫자의 값은 일치하지만 위치가 틀렸으면 B로 알려준다

 

로직을 짜는것은 할수 있겠다라고 생각했는데 사용자에게 입력값을 받는것이 문제였다

구글링하여 보니 prompt()함수를 이용하는 방법과 readline 모듈을 이용하여 콘솔을 통해 값을 입력받는 방법이 있었다

그 중 나는 readline을 이용하여 입력을 받아 게임을 진행하는것으로 구현해보았다

 

readline을 쓰기 위해서는 require로 모듈을 가져온뒤 모듈을 이용해 입출력을 위한 인터페이스 객체를 생성하면 된다

 

const readline = require("readline");

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
});

 

모듈을 사용해 입력값을 잘 받아올수 있었지만 여기서 문제가 생겼다

반복해서 입력을 받아야 하는데 rl.question으로는 유저의 입력값을 받는것을 반복할 수 없었다

 

rl.on("line", (line) => {
    rl.close();
});

을 이용하여 여러번 반복적으로 입력값을 받는 로직을 만들었지만 내가 원하는 바는 아니었다

내가 원하는 바는 '시도: 유저의 입력값'으로 띄우는 거였는데 rl.on을 받으면 내가 원하는 string을 출력하여 받을 수 없었다

 

console.log를 이용해 보았으나 유저의 입력은 줄바꿈이 된채로 받게 되었다

그래서 

 

process.stdout.write(`${turn}번째 시도: `);

을 이용하여 다시 로직을 짜보았다

 

process.stdout.write을 이용하면 console.log와 다르게 줄바꿈을 하지 않는다

그래서 rl.on위와  안에 내용에 따로 넣어 로직을 짜서 완성하였지만 원하는 바는 맞지만 내 성에 차지 않았다

 

정녕 rl.question으로 반복해서 받을수는 없는것인가라는 생각을 하고 있었을때는 '재귀'로 풀수 있겠는데....?

라는 생각이 들었다

 

재귀로 만들면 유저가 정답이 아닐때 함수를 반복하면 되니 내가 원하는 바의 완성이었다

그래서 최종적으로 

 

function baseBallGame() {
  rl.question(`${turn}번째 시도: `, (input) => {
    let { s, b } = replyCheck(input, answer);
    if (s === answerLength) {
      console.log(`${turn}번만에 맞히셨습니다.`);
      console.log("게임을 종료합니다");
      rl.close();
    } else {
      console.log(`${b}B${s}S`);
      turn++;
      baseBallGame();
    }
  });
}

으로 로직을 완성하였다

이번코드는 저번에 토이프로젝트를 할때 항해 매니저님이 조언해주신 그 로직을 관통하는 반복적으로 쓰이는 숫자가 있다면 상수에 담아 코드를 보는 사람이 코드를 봤을때도 잘 이해하게 하는것이 좋다는 것을 적용해 3을 answerLength 상수로 사용하였다

 

- 후기

깃허브 fork를 사용하는 것이 처음이니 엄청 헤매고 오류때문에 괴로웠는데 오류로 인해 fork를 하는 법을 제대로 깨닫게 되니 참 아이러니다. 사용하면서 full을 못받아 오류가 생기지 않았다면 fork된 repository와 원본 repository 차이점과 fork를 한후 clone된 코드가 어떤식으로 git에 올라가는지 알수 없었을 텐데 말이다

fork 기능을 사용하면서 remote로 저장소를 여러개 사용할 수 있다는 점도 알아가게 되어 너무 이득이다

 

깊은복사랑 얕은복사를 이해하기 위해서 선행되어야 했던 변수 선언과 데이터 할당....

처음에는 그냥 복사만 알면됬지 변수 선언과 데이터 할당을 왜 알아야하지 하고 지나갔었는데 제대로 이해가 안되어 다시 0부터 시작해 공부하니 깊은 복사랑 얕은 복사를 그제서야 제대로 이해하게 되었다 역시 모든것은 기초부터 차근차근 공부해한다는 것을 다시금 깨닫는다

이번에 제일 재밌게 공부한 부분은 호이스팅인데 너무 재밌었다 처음 예시에 대해 봤을때는 왜...? undefine...? 어째서 하는 생각으로 실행컨텍스트가 어떻게 흘러가는지 알수가 없었는데 그림도 그려보고 순서에 따라 차례대로 하나하나 되짚어 보며 공부하니 호이스팅에 대해 이해할 수 있었다 내가 예상했던 결과와 호이스팅을 따라간 결과의 차이점 그리고 내가 맞췄을때의 짜릿함이 너무 재미있었다. 약간 퀴즈 푸는 기분이었다

 

숫자야구 프로그램 처음에는 금방 만들수 있겠지 했지만 나의 모자름을 끝이 없군아... 깨달았던 프로그램짜기

일단 처음에 readline 모듈을 몰라서 모듈을 공부하는것부터가 시작이었다

공부를 한 후에도 rl.on과 rl.question은 무엇이 다른건지 계속 알아보아야 했고 rl.question을 이용하여 반복적으로 받을 수는 없을까 끝이없이 고민해야 했다. 역시 사람은 포기하지 않는것이 중요한것 같다. 안될것 같은 것도 계속해서 고민하니 결과를 얻을수 있었으니 말이다

'TIL & 일지' 카테고리의 다른 글

TIL, 06.18  (0) 2023.06.19
TIL, 06.17  (0) 2023.06.17
TIL, 06.16  (0) 2023.06.17
TIL, 06.15  (0) 2023.06.16
TIL, 06.14  (0) 2023.06.15

댓글