[리액트 공부하기]환경 설정과 연습

Ne-Yo - Because Of You (Official Music Video)

nvm -> conda에 비유 가능 node.js버전 관리자

npm -> nodejs package manager => pip에 비유 가능

npx -> npm 패키지를 설치하지 않고 실행할 수 있는 툴 예) npx create-react-app myapp -> create-react-app을 글로벌 설치 없이 바로 실행

yarn -> npm 친구

npm install -g yarn
yarn create react-app test --script

yarn으로 뭘 설치할 때는 yarn add what_you_wanna_install


class Person{
    constructor(name) {this.name = name;}
    sayHi() {return `Hi ${this.name}`;}
    static from(obj){return new Person(obj.n);}
}

const p = new Person("Kim");

q = p.sayHi();
console.log(q)
q2 = Person.from({n: 'A'});
console.log(q2);

/*
from은 Person 클래스에 정의되는 정적 메서드(static method)이다.
JavaScript에서 정적 메서드는 클래스 자체에 속하며, 클래스의 인스턴스를 생성하지 않고 클래스 이름으로 직접 호출 할 수 있다.

*/
  1. JS는 프로토타입언어이다.

프로토타입이란?

JavaScript는 프로토타입 기반 객체 지향 언어이다. 이는 클래스가 아니라 프로토타입 객체를 통해 객체 간의 상속과 동작을 구현한다는 뜻이다.

JS에서 모든 객체는 **프로토타입**이란 다른 객체를 참조하며, 이를 통해 속성이나 메서드를 공유하거나 상속받는다.

핵심 아이디어: 객체는 자신의 프로토타입 객체를 참조하고, 해당 프로토타입에 정의된 속성이나 메서드를 사용할 수 있다.

프로토타입 체인(prototype chain): 객체가 특정 속성이나 메서드를 찾을 때, 먼저 자신에게 있는지 확인하고, 없으면 프로토타입 객체를 따라 올라가며 찾는다. 이 과정을 프로토타입 체이닝이라고 한다.


2. 프로토타입의 기본 동작

JS에서 객체는 기본적으로 프로토타입 객체를 가짐. 예를 들어

모든 객체는 내부적으로 [[Prototype]]이라는 속성을 가지며, 이는 __proto__로 접근 가능하다.

현재 JS에서는 Object.getPrototypeOf(0를 사용하는 것이 권장됨.

const obj = {a : 1};
console.log(obj.toString()); //"[object Object]"

obj 객체에는 toString 메서드가 정의되어 있지 않다.

하지마 obj의 프로토탕비(Object.prototype)에 toString메서드가 정의되어 있으므로, 이를 상속 받아 호출한다.

class Person {
    constructor(name) {
        this.name = name;
    }
    sayHi() {
        return `Hi ${this.name}`;
    }
    static from(obj) {
        return new Person(obj.n);
    }
}
const p = new Person("Kim");
console.log(p.sayHi()); // "Hi Kim"

프로토타입의 역할:

Person 클래스의 프로토타입:

  • class Person은 내부적으로 생성자 함수와 프로토타입 객체를 정의함.
  • Person.prototype은 Person 클래스의 모든 인스턴스가 공유하는 프로토타입 객체이다.
  • sayHi 메서드는 Person.prototype에 정의됨. 즉, p.sayHi()를 호출 할 때 p 객체는 자신의 __proto__를 통해 Person.prototype에서 sayHi를 찾아 실행한다.

인스턴스와 프로토타입 관계:

  • const p = new Person(“Kim”)로 생성된 p 객체는 this.name = name에 의해 p.name 속성을 가진다.
  • __proto__를 통해 Person.prototype을 참조한다.
  • p.sayHi()를 호출하면:
  • p 객체 자체에는 sayHi가 없으르모, __proto__를 통해 Person.prototype.sayHi를 찾는다.
  • this는 호출한 객체 (p)를 가리키므로, this.name은 “Kim”이 되어 “Hi Kim”이 반환된다.
Ne-Yo - So Sick (Official Music Video)

정적 메서드

static from 은 사용자 정의 메서드임.

static from은 프로토타입과 직접적인 관련은 없으며, 클래스 자체(Person)에 바인딩되 메서드이다.

Person.from({n : ‘A’})는 Person클래스의 정적 메서드를 호출해 새로운 Person 인스턴스를 생성한다.

이 인스턴스도 역시 Person.prototype을 __proto__로 참조하며, sayHi같은 메서드를 상속받는다.

class Person{
    constructor(name) {this.name = name;}
    sayHi() {return `Hi ${this.name}`;}
    static from(obj){return new Person(obj.n);}
}

const p = new Person("Kim");

q = p.sayHi();
console.log(q)
q2 = Person.from({n: 'A'});
console.log(q2);

/*
from은 Person 클래스에 정의되는 정적 메서드(static method)이다.
JavaScript에서 정적 메서드는 클래스 자체에 속하며, 클래스의 인스턴스를 생성하지 않고 클래스 이름으로 직접 호출 할 수 있다.

*/

console.log(p.__proto__ === Person.prototype); // true
console.log(Person.prototype.sayHi); // ƒ sayHi() { return `Hi ${this.name}`; }

p.__proto__는 Person.prototype를 가리킨다.

sayHi는 Person.prototype에 정의되어 있으므로, 모든 Person인스턴스가 이를 공유한다.

console.log(p.toString()); // "[object Object]"

p 객체에 toString메서드가 없으므로:

  1. p.__proto__(즉, Person.prototype)에서 찾는다.
  2. Person.prototype에도 없으므로, Person.prototype.__proto__(즉, Object.prototype)에서 찾는다.
  3. Object.prototype에 toString이 정의되어 있으므로 이를 호출한다.
class Person{
    constructor(name) {this.name = name;}
    sayHi() {return `Hi ${this.name}`;}
    static from(obj){return new Person(obj.n);}
}

const p = new Person("Kim");

q = p.sayHi();
console.log(q)
q2 = Person.from({n: 'A'});
console.log(q2);

/*
from은 Person 클래스에 정의되는 정적 메서드(static method)이다.
JavaScript에서 정적 메서드는 클래스 자체에 속하며, 클래스의 인스턴스를 생성하지 않고 클래스 이름으로 직접 호출 할 수 있다.

*/

console.log(p.__proto__ === Person.prototype); // true
console.log(Person.prototype.sayHi); // ƒ sayHi() { return `Hi ${this.name}`; }

console.log(p.toString()); // "[object Object]"


Person.prototype.newMethod = function() {
    return "New method!";
};
console.log(p.newMethod()); // "New method!" (기존 인스턴스에도 반영됨)

JavaScript가 프로토타입 기반 언어라는 것은 다음과 같은 특징을 지닌다.

  • 상속의 유연성: 클래스 기반 언어(Java, C++)는 고정된 클래스 계층 구조를 사용하지만, JavaScript는 프로토타입을 동적으로 변경할 수 있어 유연하다.
Person.prototype.newMethod = function() {
    return "New method!";
};
console.log(p.newMethod()); // "New method!" (기존 인스턴스에도 반영됨)
  • 메모리 효율성: 메서드는 각 인스턴스 마다 생성되지 않고, 프로토타입 객체에 저장되어 모든 인스턴스가 공유한다. 예를 들어, sayHi는 Person.prototype에 하나만 존재하며, 모든 Person 인스턴스가 이를 참조한다.
  • 동적 추가 가능: 실행 중에 프로토타입에 메서드나 속성을 추가할 수 있다.

class MyComponent extends React.Component {
    render() {
        return <div>Hello</div>;
    }
}

React에서는 함수형 컴포넌트가 주로 사용되지만, 클래스 컴포넌트를 사용할 때는 프로토타입이 동일하게 적용된다.

  • MyComponent의 인스턴스는 MyComponent.prototype에서 render 메서드를 상속받는다.
  • React.Component 자체도 프로토타입을 통해 setState, forceUpdate같은 메서드를 제공한다

하지만 함수형 컴포넌트 프로토타입과 직접 관련이 없으며, 클로저와 훅을 통해 상태와 동작을 관리한다. 이 때문에 React개발에서는 프로토타입을 직접 다룰 일이 적다.

function Animal(name){
    this.name = name
}


Animal.prototype.say = function(){
    return `${this.name} says hi!`;

}


const dog = new Animal("Dog");
console.log(dog.say()); //"Dog say hi!"
console.log(dog.__proto__ === Animal.prototype);

JS는 좀 괴상하다.

new Animal(“Dog”)가 하는 일

new 키워드를 사용하면 JavaScript 내부에서 다음 단계를 거친다.

  1. 새로운 객체 생성: 빈 객체 {}가 생성된다.
  2. 프로토타입 연결: 새 객체의 [[Prototype]](즉, __proto__)이 Animal.prototype을 가리키도록 설정된다.
  3. 생성자 함수 실행: Animal 함수가 실행되며, this는 새로 만든 객체에 바인딩된다. 그래서 this.name = name은 새 객체에 name속성을 추가한다.
  4. 객체 반환 : 새 객체가 반환되어 dog 변수에 할당된다.
  5. const dog = new Animal(“Dog”);는 name : “Dog”속성을 가진 객체를 만들고, Animal.prototype을 프로토타입으로 연결해 say메서드를 상속받는다.
SawanoHiroyuki[nZk]:mizuki「Avid」×TVアニメ「86―エイティシックス―」Collaboration Movie

Animal.prototype은 생성자 함수가 가진 프로토타입 객체를 가리킨다.

이 객체는 Animal로 생성된 모든 인스턴스들이 공유하는 메서드와 속성을 저장하는 곳이다.

즉, Animal.prototype은 인스턴스들이 상속 받을 템플릿 역할을 한다.

Animal.prototype.say = function(){ … }는 모든 Animal인스턴스가 say메서드를 공유하도록 만든다.

obj.__proto__:

obj는 new Animal()로 생성된 인스턴스 객체이다.

__proto__는 객체가 자신의 프로토타입 객체를 참조하는 내부 속성([[prototpye]])에 접근하는 비표준 방식이다. 현대 JavaScript에서는 Object.getPrototypeOf(obj)를 사용하는 것이 권장된다.

Animal.prototype은 생성자 함수의 속성으로, 인스턴스들이 공유할 프로토타입 객체를 정의한다.

obj.__proto__는 인스턴스가 참조하는 프로토타입을 가리키며, 이는 보통 Animal.prototype과 같다.

10 Utsukushiki Mono | Sound Horizon | Live | English Sub

클로져란?

정의: 클로저는 함수와 그 함수가 정의된 렉시컬 스코프(lexical scope)를 결합한 것. 즉, 함수가 자신이 선언된 환경의 변수(외부 변수)를 기억하고, 그 변수에 접근할 수 있는 능력을 말한다.

“함수와 함수가 선언된 어휘적 환경의 조합”


렉시컬 스코프(Lexical Scope)란 함수를 어디에 선언하였는지에 따라 상위 스코프가 결정되는 것을 말한다.

자바스크립트를 포함한 대부분의 프로그래밍 언어는 렉시컬 스코프를 따르며, 이를 정적 스코프(Static Scope)라고 부르기도 함.

1.자바스크립트는 정적 스코프 언어이다.

즉, 함수가 어디서 호출되었는가가 아니라 어디서 정의되었는가를 기준으로 스코프 체인이 형성된다.

2.스코프체인(Scope Chain)

  • 함수 내부에서 변수를 찾을 때, 먼저 자기 자신의 스코프에서 찾고, 없으면 상위(외부) 스코프로 올라가며 탐색한다.
  • 이 과정은 함수가 선언될 때 결정되며, 호출 위치와는 상관없다.

3. 호출 위치와 무관

  • 함수가 전혀 다른 위치에서 호출 되더라도, 변수를 찾는 기준은 함수가 작성된 위치의 스코프이다.
const x = 10;

function outer() {
  const x = 20;

  function inner() {
    console.log(x);
  }

  return inner;
}

const fn = outer();
fn(); // 20

inner 함수는 outer안에서 정의되었기 때문에, inner의 스코프 체인은 outer -> 전역 순으로 고정된다.

따라서 fn()을 전역에서 실행해도, inner는 자신이 정의된 outer의 x = 20에 접근한다.

요약하면 JS의 렉시컬 스코프는 함수가 “선언된 위치”를 기준으로 변수의 유효 범위를 결정하는 규칙이다.

tayori - 風のたより (Official Video)

클로저는 함수와 그 함수가 정의된 렉시컬 스코프를 결합한 것이다. 즉, 함수가 자신이 선언된 환경의 변수(외부 변수)를 기억하고, 그 변수에 접근할 수 있는 능력을 말한다.

특징:

  • 스코프 유지: 클로저는 함수가 선언된 환경의 변수를 “캡쳐”해 이후에도 접근가능하다. 이는 함수가 선언된 환경의 변수(외부 변수)를 기억하고, 그 변수에 접근할 수 있는 능력을 말한다.
  • 상태 유지: 클로저는 외부 변수의 상태를 유지 할 수 있어, 프라이빗 데이터나 캐싱 같은 패턴에 유용하다.
  • 언어적 맥락: JavaScript, Python, Scheme같은 언어에서 클로저가 강력하게 지원된다. JavaScript에서는 함수가 일급 객체(first-class citizen)이기 때문에 클로저가 자주 사용된다.
function createCounter() {
  let count = 0; // 외부 스코프의 변수
  return function() {
    return ++count; // 내부 함수가 count를 기억
  };
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2

보시다시피 내부변수로 선언된 count를 기억한다.

createCounter는 내부 함수를 반환하며, 내부 함수는 count 변수를 기억한다. 이를 클로저라고 부른다.

count는 외부에서 접근할 수 없지만, 반환된 함수가 이를 계속 참조한다.

특징프로시저클로저
정의작업을 수행하는 독립적 코드 블록함수와 그 함수가 접근하는 외부 스코프의 결합
상태 유지상태를 유지하지 않음 (외부 변수 의존 시 가능)외부 스코프의 변수를 기억하고 상태 유지 가능
스코프호출 시 새로운 스코프 생성선언 시 스코프를 캡처해 유지
용도단순 작업 수행(입출력, 부수 효과)데이터 은닉, 상태 유지, 비동기 처리 등
예시 언어C, Pascal, JavaScript (단순 함수)JavaScript, Python, Scheme
반환값필수 아님 (void 가능)보통 함수 반환 (내부 함수가 외부 변수 참조)

function addDiv(text) {
  const div = document.createElement('div');
  div.textContent = text;
  document.body.appendChild(div);
}
addDiv('Hello'); // DOM에 div 추가

프로시저 예시


import { useState } from 'react';

export default function CounterButton() {
  const [clicks, setClicks] = useState(0);

  return (
    <button onClick={() => setClicks(clicks + 1)}>
      {clicks === 0 ? 'Click me' : `Clicked ${clicks} times`}
    </button>
  );
}

클로져 예시