[TIL 2023.02.28] const 선언 (배열, 객체) / useRef

김헤일리·2023년 2월 28일
0

TIL

목록 보기
36/46
post-custom-banner

지식 보충을 위해 조금씩 정리를 해보자! #6


1. const 선언

const 선언은 블록 범위의 상수를 선언합니다. 상수의 값은 재할당할 수 없으며 다시 선언할 수도 없습니다.

출처: MDN - const

const cars = ["Saab", "Volvo", "BMW"];
cars = ["Toyota", "Volvo", "Audi"]; 
// Uncaught TypeError: Assignment to constant variable.
  • 위의 예시를 보면, "cars"라는 상수에 할당한 배열을 재할당할 경우, "상수에 할당되었다" 라는 에러 메세지가 출력된다.
const cars = ["Saab", "Volvo", "BMW"];
const cars = ["Toyota", "Volvo", "Audi"]; 
// Uncaught SyntaxError: Identifier 'cars' has already been declared
  • 그리고 "cars"라는 상수를 한번 더 이용해서 새로운 배열을 선언할 경우, "cars라는 선언문은 이미 사용되었다" 라는 에러 메세지가 출력된다.

위의 두 예시는 배열을 "cars"라는 상수 선언문을 이용하여 아예 새로운 값으로 재할당을 하거나 재선언을 하려고 했기 때문에 에러가 발생하였다. 배열이나 객체는 참조 자료형이기 때문에 속성을 바꾸는 것이 아니라 재할당/재선언은 참조된 주소값을 전부 바꾸는 것과 다름 없기 때문에 에러가 발생한다.

// const를 사용해서 배열을 선언할 수 있다.
const cars = ["Saab", "Volvo", "BMW"];

// 배열을 다시 재선언/재할당하는 것이 아니라 배열의 속성을 수정하는 것은 가능하다.
cars[0] = "Toyota";
// 값: ["Toyota", "Volvo", "BMW"]

// 배열의 속성을 하나 추가할 수도 있다.
cars.push("Audi");
// 값: ["Toyota", "Volvo", "BMW", "Audi"]

속성을 추가/변경/삭제 하는 것은 할당된 메모리의 주소값의 문제는 아니기 때문에 에러 없이 실행된다.

사실 배열/객체의 특수성은 자료형 타입이 다르기 때문에 발생한다. 문자열, 숫자, 불린 값 같은 원시 자료형과 배열, 객체, 함수 같은 참조 자료형은 메모리에 저장되는 방식부터 다르기 때문이다.

변수에 원시 자료형 데이터를 할당할 경우, 해당 데이터 자체가 "STACK" 이라는 메모리 공간에 저장된다. 그리고 해당 변수는 할당된 값의 주소를 "STACK"에서 관리한다.

하지만 참조 자료형의 데이터를 변수에 할당할 경우, 해당 데이터는 "HEAP"이라는 메모리에 공간에 저장된다. 그리고 "HEAP"에 저장된 데이터의 주소는 "STACK"으로 복사되고, 해당 변수는 데이터를 불러올 때 "STACK"에서 복사된 데이터를 참조하게 된다.

  • 위의 그림을 보면, "fruit", "copyOfApple", "apple"과 같은 객체의 값은 "HEAP"에 저장되어있고, 힙에 저장된 값을 주소의 형식으로 (#0001) "STACK"에 복사하여 가져온 것을 확인할 수 있다.
  • 복사해서 가져왔기 때문에 "fruit", "copyOfApple", "apple"은 모두 다른 이름으로 선언되어있지만, 참조하고 있는 값은 #0001로 셋다 동일하다.
// Primitive Types (원시 자료형)
let name = 'John';
let copyOfName = name;

name = 'Adam'; // let은 재할당이 가능하다

console.log(name); // => 'Adam'
console.log(copyOfName) // => 'John' 
  • copyOfName이 선언되었을 때 참조하는 값은 'John"이다.
  • 문자열같은 자료형은 원시 자료형이기 때문에 name이 "Adam"으로 재할당되어도 copyOfName은 맨 처음 자료를 배꼈을 때의 시점인 "John"이 된다.
// Reference Types (참조 자료형)
let apple = {name: 'Apple', price: 120}
let copyOfApple = apple;

apple.price = 130;

console.log(apple) // => {name: 'Apple', price: 130}
console.log(copyOfApple) // => {name: 'Apple', price: 130}
  • copyOfApple의 경우, apple이 할당되었다.
  • 하지만 둘 다 HEAP에 있는 같은 데이터를 참조하기 때문에, 원시 자료형 예시와는 다르게 apple.price로 바뀐 속성이 apple 뿐만이 아니라 copyOfApple에도 동일하게 적용된 것을 볼 수 있다.
  • 객체나 배열에서 불변성을 지켜야하는 이유가 바로 참조 자료형의 경우 "STACK"에서 참조하는 주소값이 같이 때문에 얕은 복사를 사용할 경우 원본도 영구적으로 변경되기 때문이다.

결론!

const로 정의한 상수는 값을 재할당하거나 재선언할 수 없다. 하지만 배열이나 객체는 원시형이 아니라 참조 자료형이기 때문에 여기서 말 하는 재할당/재선언이라는 것은, 사실 상 선언된 변수가 참조하는 주소의 값이 변할 수 없다는거지 선언된 배열이나 객체의 속성이 변할 수 없다는 뜻이 아니다.



2. useRef()

JavaScript에선 특정 DOM 요소를 선택해야 하는 상황에 getElementById, querySelector 같은 DOM Selector 함수를 사용한다.

리액트를 사용하는 프로젝트에서도 가끔씩 DOM 을 직접 선택해야 하는 상황이 발생 할 때도 있다.
특정 엘리먼트의 크기를 가져와야 한다던지, 스크롤바 위치를 가져오거나 설정해야된다던지, 또는 포커스를 설정해줘야된다던지 등의 다양한 상황이 생긴다.

추가적으로 Video.js, JWPlayer 같은 HTML5 Video 관련 라이브러리, 또는 D3, chart.js 같은 그래프 관련 라이브러리 등의 외부 라이브러리를 사용해야 할 때에도 특정 DOM 에다 적용하기 때문에 DOM 을 선택해야 하는 상황이 발생 할 수 있다.

DOM 요소를 직접 선택할 때 React에선 Ref 설정을 활용하고, 함수형 컴포넌트에서 ref 를 사용 할 때에는 useRef 라는 Hook을 사용한다.

function App() {
  const inputElement = useRef();
  // 1. 변수를 선언하고 해당 변수에 useRef()를 할당한다.

  const focusInput = () => {
    inputElement.current.focus();
    // 2. ref 속성을 갖고 있는 inputElement에 focus가 가도록 만드는 함수
    // 2-1. Ref 객체의 .current 값은 설정한 DOM 을 가르키게 된다
    // 2-2. 해당 함수가 실행되면 inputElement를 ref 속성으로서 가지고 있는 요소에 focus가 잡히게 된다.
  };

  return (
    <>
      <input type="text" ref={inputElement} />
	  // 3. 위에 변수 inputElemet에 useRef()를 할당했기 때문에 원하는 DOM 요소에 ref 속성을 적용할 수 있다.
      <button onClick={focusInput}>Focus Input</button>
	  // 4. 버튼 클릭 시 focusInput이 실행된다.
    </>
  );
}

다만 useRef()는 내용이 변경될 때 변경된 사항이 감지되진 않는다..current 프로퍼티를 변형하는 것이 리렌더링을 발생시키지 않는다는 의미이다. useRef()는 컴포넌트의 속성만 조회/수정한다



출처:

profile
공부하느라 녹는 중... 밖에 안 나가서 버섯 피는 중... 🍄
post-custom-banner

0개의 댓글