책 읽다가 코딩하다 죽을래

리액트 심화반 - 1주차 개발일지(scope, Object, Function) 본문

GD프로젝트/개발일지

리액트 심화반 - 1주차 개발일지(scope, Object, Function)

ABlue 2021. 7. 22. 22:59

 

 

GD프로젝트 리액트 심화반

저번 주 프론트엔드의 꽃 수업을 다 들은 후

이번 주에는 리액트 심화반이라는 새로운 강의를 듣게 되었다.

이번 5주 차에 있었던 일을 바로 기술해보겠다.

 

 

목차

1. 새롭게 배운 내용(scope, Object, Function)

2. 1주 차 과제

 

 


1. 새롭게 배운 내용


1 - 1 Scope

 

더보기

 

scope는 어떤 하나의 데이터가

어느 정도의 범위를 가져 영향을 주는지의 대한 개념이다.

 

 

function fn(){
	int a = 1;
}
fn();
system.out.println(a);

 

자바나 c++을 배웠다면

위의 코드가 이상하다고 생각할 것이다.

왜냐하면 함수 밖에서 함수 안에 있는 a를 접근하면 분명 오류가 일어나기 때문이다.

 

즉 함수 안에서 선언된 a는 밖에서 접근할 수 없는 것

a의 scope는 함수 fn 범위 안이라고 정의할 수 있다.

 

자바스크립트에서의 scope는 다양해서 헷갈리기 쉽다.

 

 

자바스크립트 내에 데이터 타입은 크게

var, let, const로 나뉜다.

여기서 let과 const는 스코프가 같으니

var와 let, const 이렇게 설명하겠다.

 

 


var scope


 

1. var의 스코프는 함수 수준 스코프를 가진다.

function fn(){
	name = "ablue";
    console.log(name);
	var name;
}

 

  

 위의 코드처럼 

선언하기 이전에도 사용할 수 있다.

 

왜냐하면 자바스크립트 엔진이 fn의 함수를 실행하게 되면

실행하기 전에 fn 함수 안에 있는 모든 var 변수를 찾아서

실행 컨텍스트에 집어넣은 후 변수에 필요한 메모리를 미리 할당하기 때문이다.

그래서 선언하기 전에도 사용할 수 있다.

그런데 선언만 호이스팅이 되고 할당하는 것은 호이스팅이 되지 않는다.

 

 

function fn(){
    console.log(name);
    name = "ablue";
	var name;
}

 

이런 식이면 콘솔에는 undefined가 뜬다.

name이란 변수는 있는데 값은 할당되기 이전이기 때문이다. 

 

 

 

2. var는 재선언이 가능하다.

 

function fn(){
	var name = "ablue";
	var name = "red";
    console.log(name);		// red
}
fn();

 

var는 재선언이 가능하므로

이런식의 코드는 already define 오류가 일어나지 않으며 정상 작동된다.

콘솔에선 최근 초기화됐던 red가 표시된다.

 

 

 

 

이런 말도 안 되는 스코프 방식으로 인해 개발자들은

var로 인해 오류가 일어나면 찾기가 힘들어서

var보다 let을 선호한다.

 

var는 재선언이 가능하므로

이런 식의 코드는 already define 오류가 일어나지 않으며 정상 작동된다.

콘솔에선 최근 초기화됐던 red가 표시된다.

 

 

이런 말도 안 되는 스코프 방식으로 인해 개발자들은

var로 인해 오류가 일어나면 찾기가 힘들어서

var보다 let을 선호한다.

 


const, let scope


 

1. let과 const는 재선언이 불가능하다.

function fn(){
	const a = 1;
    const a = 2;	// Uncaught SyntaxError: Identifier 'a' has already been declared
}

 

 

2. const와 let은 block 수준 스코프를 가진다.

 

 

여기서 block 뭐냐 하면 {}를 말한다.

 

function fn(){
	const ary = [1,2,3];
    console.log(i);		// Uncaught ReferenceError: i is not defined
    for(let i = 0; i < ary.length; i++){
        console.log(ary[i]);
    }
}
fn();

 

for 구문 block 안에서 선언된 i는

for 구문 block 밖에서는 사용될 수 없다.

 

그런데 이 상황을 보자

 

function fn(){
    console.log(i);		// Uncaught ReferenceError: Cannot access 'i' before initialization
    let i  = 1;
}
fn();

 

이 코드도 위쪽의 코드처럼 안 되는 것은 뻔히 알고 있다.

근데 아직 i 선언 전이니까 is not defined 오류가 일어나야지

i 선언 전엔 접근할 수 없다는 레퍼런스 오류가 나고 있다.

 

그렇다. 자바스크립트 엔진은 let, const 선언 전에도 그 존재를 알고 있다.

여기서 TDZ라는 중요한 개념이 등장한다.

 

 


TDZ(Temporal Dead Zone) 일시적 사각지대


let과 const도 호이스팅이 된다.

자바스크립트 엔진은 함수가 시작하면

함수 내에 var 뿐만 아니라 let과 const도 찾아내어

호이스팅을 하여 실행컨텍스트에 집어넣는다.

 

하지만 선언한 후, 초기화 단계에서 메모리를 확보하는데

let과 const는 초기화 전까지는 메모리에 공간을 확보하지 않는다.

(var는 선언하자마자 메모리에 공간을 확보합니다.)

그래서 호이스팅이 되어 is not defined 에러는 나지 않고

변수가 참조하는 메모리를 찾지 못해 레퍼런스 에러가 일어난다.

 

정리하자면 let, const는 호이스팅이 되지만

선언 전까지는 TDZ 상태가 되는 것이다.

TDZ 상태에서는 액세스를 하지 못한다.

 

function fn(){
    console.log(i);		// 자바스크립트 엔진 : ? i라는 변수는 있는데 얘가 참조한 메모리가 없는데??
    let i  = 1;
}
fn();

 

사실상 let, const를 사용할 때는 이전 c++, 자바, 파이썬을 사용할 때처럼

생각하면서 코드를 짜도 문제는 없다.

 

하지만 이 개념을 알아야 하는 이유는 작동 원리도 알아야 고수가 될 수 있는 것이고 면접에서도 물어보기 때문이다.

 

 

 

 

 

 

 

 

 

 

 

 

1 - 2 객체

 

 

더보기

 


객체의 선언 및 접근


 

 

 

 

const obj1 = {};
const obj2 = new Object();
const ablue = {name:'ablue', age: 26}; // 선언과 초기화를 동시에 하는 법

 

 

객체는 "key" : value로 이루어져 있다.

c언어의 구조체와 자바의 해쉬 맵과 파이썬의 딕셔너리와 비슷하다.

 

그런데 여기서 key는 무조건 string형이며 value는 어떤 타입이든 상관없다.

 

 

console.log(ablue['name']);     //이렇게 배열형태로 데이터에 접근할 수 있다. 주의할 점은 배열의 인덱스는 String타입으로 줘야한다. name으로 주면 undefined가 뜬다.
ablue['age'] = true;     //이런식으로 데이터를 설정할때도 쓰인다.

console.log(ablue.name);		// 배열형태가 아닌 c언어처럼 .(닷) 을 붙여 해당 속성에 접근할 수 있다.
ablue.name = "홍길동";

 

 


객체의 메모리 저장 방식


 

자바스크립트는 8가지 기본 자료형을 지원한다.

 

1. 정수, 부동 소수점을 저장하는 숫자형(Number) : -(2^53-1) ~ (2^53-1)까지 지원
Number의 범위를 넘어서는 숫자를 저장하는 BigInt형
문자열을 저장하는 문자형(String)
논리 값 (true/false. boolean형 )
값이 할당되지 않음을 나타내는 독립 자료형 undefined
값이 존재하지 않음을 나타내는 독립 자료형 null
복잡한 자료구조를 저장하는 데 쓰는 객체형
고유 식별자를 만들 때 쓰는 심볼형  

 

여기서 객체형을 제외한 다른 데이터 타입들은 원시형이다

원시형이란 말은 오직 하나의 값만 저장되는 것이다.

 

오직 하나의 값만 저장하게 되니 실제 메모리에선

 

 

 해당 사진의 출처는 인프런 정재남 선생님의 JS Flow 강의에 있습니다.

강의 보러 가기 [클릭]

 

이런 식으로 저장이 된다.

변수 값을 사용할 때마다 해당 변수명을 찾아 값이 저장된 주소를 확인하고

그 주소에 찾아가 실제 값을 읽는다.

 

 

let a = 10;
let b = a;

console.log(a === b) // true

a = 5;
console.log(b);		 // 10
console.log(a === b) // false

 

그래서 b에다 a값을 대입하면 똑같아진다.

왜냐하면 메모리 상에서 b가 가리키는 값과 a가 가리키는 값은 똑같기 때문이다.

 

하지만 a와 b가 서로 같은 곳을 가리키는 것은 아니기 때문에

a와 b 중 어느 하나의 값이 달라진다 해서 나머지 변수는 달라지진 않는다.

모든 원시형 데이터 타입은 이런 특징을 갖는다.

 

 

그런데 객체는 다른 방식으로 메모리에 저장된다.  

 

 

 

 

 객체가 초기화되면 주소 값이 참조된 곳에 값이 들어있는 것이 아니라

객체의 속성 값이 저장된 주소들의 집합체의 주소가 들어있다.

 

 

 

 

 

그래서 객체의 객체를 대입하면

원시 데이터 타입처럼 메모리 사용량이 2배가 되진 않는다.

이렇게 같은 곳(1011번)을 가리키게 된다.

 

var obj = {a:1, b:'b'};
var obj2 = obj;
obj2.a = 10;
console.log(obj.a);		// 10

 

 

그래서 한쪽의 객체의 속성값을 초기화하면 반대쪽 객체의 해당 속성 값도 달라진다.

왜냐하면 서로 같은 곳을 가리키기 때문이다.

 

그러면 같은 곳을 가리키지 못하게 만들면서 복사하는 방법은 

... (sprread operator 펼침연산자) 을 사용한다.

 

var obj3 = ...obj;
obj3.a = 10;
console.log(obj.a);		// 1

 

우리는 이런 복사 방식을 얕은 복사(shallow copy)라고 부른다.

var obj3 = obj;는 같은 곳을 가리키게 만드니 깊은 복사(deep copy)다.

 

펼침 연산자는 배열에도 쓰인다.

배열에도 쓰인다는 것은 배열도 객체의 메모리 저장 방식을 따른다.

const ary = [...ary2];

 

또는 Object의 assign 메소드로 얕은 복사가 가능하다.

 

 

const user = {name:'ablue', age:26};
const user2 = {};
Object.assign(user2, user);

 

 

또한 이런 객체의 메모리 저장 방식 때문에 이런 것도 가능하다.

 

const obj = {a:1, b:'b'};

obj.b = 'c';

 

 

 분명 const로 상수 취급했는데 속성 값을 초기화할 수 있다.

왜냐하면 속성값을 다르게 했다 해도 obj의 주소 값이 가리키는 속성값이 담긴 집합체가 담겨있는 주소값이 달라지는 것이 아니기 때문이다.

이 말이 되게 어렵게 느껴지는 게 그림으로 설명해보자면 이렇다.

 

 

 

obj.b의 새로운 값으로 초기화하면 obj의 주소 값(413)은 달라지지 않고 b의 해당하는 주소 값(1013)만 달라지게 된다. 즉 자바스크립트 엔진에선 obj.c가 초기화된다고 해도 똑같다고 생각하는 것이다.

 

const는 주소 값만 달라지지 않으면 초기화되었다고 판단하지 않는다.

 

 

const obj = {name:'ablue', age:26};
obj = {name:'바뀐이름', age:27};		// Uncaught TypeError: Assignment to constant variable.

 

 하지만 전부를 초기화하는 것은 주소 값이 달라져서 가리키는 것이 달라지기 때문에 오류가 일어난다.

 

 

 

const ary = [1,2,3];
ary[0] = 2;	// 오류가 일어나지 않음
ary = [4,5,6]; // Uncaught TypeError: Assignment to constant variable.

 

배열도 마찬가지이다. 

 

 

자바스크립트 엔진은값이 달라졌는지 않았는지 판단할 때 Object is 비교 알고리즘을 통해 판단하는데

이 사고방식에 대해 자세하게 알고 싶다면 JS 공식 문서의 Object is를 읽어보자

 

 

 

 

Object is 공식문서 보러 가기 [클릭]


객체의 순차 접근


 

객체의 순차 접근은 for .. in 과 for .. of 가 있다.

 

 

for .. in

 

//for (key in obj)
const ablue = {age: 26, hobby:'coding', dream:'FrontEnd'};
for(key in ablue){      //향상된 for구문과 같다 
    console.log(key);
}

// age		//그러나 for..in구문은 객체의 key값엔 접근가능하나 key의 value에는 접근할 수 없다. 접근할려면 ablue[key]를 사용해야한다.
// hobby
// dream

for(key in ablue){
    console.log(ablue[key]);
}

// 26
// coding
// FrontEnd

 

for .. of 

 

 

// for(value of iterable) 순서가 있는 객체를 출력할 때 쓴다
const array = [1,2,4,5];
for(value of array){
    console.log(value);		// 1 2 4 5
}

 

 

 

1 - 3 Function

 

 

더보기

 

자바스크립트의 함수도 다른 언어와 달리 신기한 특징을 갖는다.

 

자바스크립트 함수는 값(value)이 될 수 있다.

 

 

const printFn = function(){
	console.log('fn');
}

printFn;

// ƒ (){
//  	console.log('fn');
// }

printFn();
// fn

 

 

그래서 이렇게 변수에 저장할 수 있으며,

변수에 () 을 붙이면 함수가 실행되고 변수만 적으면 함수 내용을 문자형으로 출력한다.

 

 

함수를 정의하는 데에는 함수선언문과 함수 표현식이 있다.

 

// 함수 선언문
function printFn(){
	console.log('fn');
}

// 함수 표현식(anonymouns funciton)
const printFn = function(){
	console.log('fn');
}

// 함수 표현식(arrow funciton)
const printFn = () => {
	console.log('fn');
}

 

함수 선언문 과 함수 표현식의 특징은 다음과 같다.

 

  • 함수 선언문
  1. 독립된 구문으로 존재
  2. 코드 블록이 실행되기 전에 미리 호이스팅 되어 블록 내 어디서든 사용이 가능
  • 함수 표현식
  1. 함수가 표현식의 일부로 존재
  2. 실행 컨텍스트가 표현식에 도달해야 만들어진다.(선언만 호이스팅 되고 메모리엔 들어가지 않는다.)

 

함수를 값처럼 전달할 때, 인수로 넘겨주는 함수를 콜백 함수라고 한다.

 

function randomQuiz(answer, printMeToo, printNo){
    if(answer === 'I love you'){
        printYes();
    } else {
        printNo();
    }
}

const printMeToo =  function(){
    console.log('me too');
}
//named function    어나니머스 펑션과는 다르게 변수에 할당된 함수가 이름을 갖는 것
const printNo =  function print(){
    console.log('no');
}

randomQuiz('wrong', printMeToo, printNo);
randomQuiz('love you', printMeToo, printNo);

 

 

 

 


2. 1주 차 과제


 

더보기
1주 차 과제 결과물

 

 

1주 차 과제는 최소 컴포넌트 설계하기였다.

 

폴더구조를 보면 알 수 있듯이

html 태그별로 일일이 컴포넌트를 만들어

유지 보수하기 쉽게 만드는 전략이다.

 

처음에는 만들기 어렵지만

컴포넌트의 규모를 적당히 잡고 체계화시키면

다음 페이지 작업할 때 시간이 많이 단축된다.