본문 바로가기

웹 개념

[Web] 기술 인터뷰 회고 - 답을 제대로 못했던거 정리 - 버블링, 클린업 코드, useRef vs useState, 비동기 코드를 자바스크립트에서 쓸 수 있는 이유

728x90

문제의 의도를 직적접으로 묻지 않고, 코드나 상황이 주어지고, 의도를 알아채고 답변하는 형식으로 코딩 테스트 문제가 주어졌음.

문제는 정확히 기억 안나기에 문제를 예를 들어 설명은 못하겠고, 알아야 했던 개념을 뒤늦게 몇 개 떠올려서 다시 정리해보는 포스팅임

 

1. 버블링

 

console 찍는 값으로 e.target.focus, e.currentTarget.focus 를 value로 받는 두 버튼을 받는 부분인 문제가 있었음.

코드를 보고 의도도 모르고 주저리주저리 말했었는데 알고보니 버블링 관련 문제 였음.

 

* 버블링이란? 

버블링(Bubbling)은 이벤트 처리에서 발생한 이벤트가 해당 요소에서 시작하여 상위 요소로 전파되는 현상을 말합니다. 즉, 하위 요소에서 발생한 이벤트가 부모 요소, 그리고 부모 요소의 상위 요소로 계속해서 전파되는 것을 의미합니다. 

=> 사실 모르는 사람이 없겠지.

 

다만 내가 의도 파악도 못할정도로 간과하고 있었다....문제가 심각 그래서 정리하고자 한다.

 

<!DOCTYPE html>
<html>
  <head>
    <title>버블링 예시</title>
  </head>
  <body>
    <div id="outer">
      <button id="innerBtn">버튼</button>
      <div id="inner">
        <button id="btn">버튼</button>
      </div>
    </div>

    <!-- 스크립트 파일을 불러옵니다. -->
    <script src="script.js"></script>
  </body>
</html>

 

// div 요소들을 선택합니다.
const outer = document.getElementById("outer");
const inner = document.getElementById("inner");
const btn = document.getElementById("btn");
const innerBtn = document.getElementById("innerBtn");

// 각 요소에 대해 다른 이벤트 리스너를 등록합니다.
btn.addEventListener("click", function (event) {
  console.log("버튼에서 클릭 이벤트가 발생했습니다.");
  console.log(
    "이벤트가 버튼에서 시작하여 전파되는 경로:",
    event.composedPath()
  );
});

innerBtn.addEventListener("click", function (event) {
  console.log("innerBtn 에서 클릭 이벤트가 발생했습니다.");
  console.log(
    "이벤트가 innerBtn 에서 시작하여 전파되는 경로:",
    event.composedPath()
  );
  event.stopPropagation(); // 이벤트 전파 중지
});

 

innerBtn 요소의 클릭 이벤트에서 event.stopPropagation() 메서드를 사용하여 이벤트 전파를 중지하였기 때문에, innerBtn에서 클릭 이벤트가 발생할 때는 해당 이벤트가 버블링되지 않고 innerBtn 요소에 대한 이벤트 리스너만 실행됨

따라서 innerBtn을 클릭하면 "innerBtn에서 클릭 이벤트가 발생했습니다."와 함께 이벤트가 innerBtn에서 시작하여 전파되는 경로가 출력됩니다. 이후 버튼을 클릭하면 "버튼에서 클릭 이벤트가 발생했습니다."와 함께 이벤트가 버튼에서 시작하여 전파되는 경로가 출력됨

이런식으로 이벤트가 발생하는게 이벤트 버블링임...

 

2. useEffect 클린업

 

클린업 코드(cleanup code)는 주로 useEffect 훅을 사용할 때 부수 효과를 처리하는 함수 내부에 작성됩니다. 이때, cleanup 코드는 컴포넌트의 마운트(mount)와 언마운트(unmount) 시에 실행됩니다. 아래에서 설명드리도록 하겠습니다.

컴포넌트 라이프사이클에서 useEffect 훅과 cleanup 코드의 실행과정은 다음과 같습니다:

컴포넌트가 렌더링되면 useEffect 훅이 실행됩니다.
useEffect 함수 내부에서 API 호출이나 기타 비동기 작업이 실행될 수 있습니다.
컴포넌트가 마운트될 때만 실행되어야 하는 경우, useEffect 함수 내부에서 빈 의존성 배열 []을 전달하여 설정합니다. 이렇게 하면 컴포넌트가 처음 렌더링될 때만 한 번 실행됩니다.

import React, { useState, useEffect } from 'react';

const MyComponent = () => {
  const [data, setData] = useState(null);

  // 컴포넌트가 마운트될 때 실행되는 부수 효과
  useEffect(() => {
    console.log('컴포넌트가 마운트되었습니다.');

    // 데이터를 가져오는 API 호출
    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(data => setData(data));

    // cleanup 함수
    return () => {
      console.log('cleanup 함수가 실행됩니다.');

      // 컴포넌트가 언마운트되기 직전에 실행되는 코드
      // 이곳에서는 cleanup 작업을 수행합니다.
      // 예를 들어, 구독 해제, 이벤트 리스너 제거 등
    };
  }, []); // 빈 배열을 전달하여 컴포넌트가 마운트될 때만 실행

  // ...

  return (
    <div>
      {/* 데이터를 이용한 JSX 표시 */}
      {data && (
        <ul>
          {data.map(item => (
            <li key={item.id}>{item.name}</li>
          ))}
        </ul>
      )}
    </div>
  );
};

export default MyComponent;


컴포넌트가 언마운트될 때 cleanup 코드를 실행하기 위해 useEffect 함수 내부에서 return 문을 사용하여 cleanup 함수를 반환합니다.
cleanup 함수는 컴포넌트가 사라지기 직전, 즉 언마운트(unmount)되기 전에 실행됩니다.
cleanup 함수 내부에는 컴포넌트가 마운트되었을 때 등록한 작업들을 정리하는 코드를 작성합니다. 예를 들어, 구독 해제, 이벤트 리스너 제거, 타이머 해제 등의 작업을 수행할 수 있습니다.
아래는 컴포넌트의 마운트와 언마운트 시에 cleanup 코드를 사용하는 예제입니다

 

위의 예제에서 useEffect 훅은 빈 의존성 배열 []을 전달하여 컴포넌트가 마운트될 때만 실행되도록 설정되었습니다. 마운트 시에 API를 호출하고 데이터를 가져오는 작업이 이루어집니다. 또한, cleanup 함수가 컴포넌트가 언마운트되기 직전에 실행되며, cleanup 작업이 필요한 경우에는 해당 작업을 수행하도록 구현되었습니다.

 

3. useRef vs useState 차이

 

useRef와 useState는 둘 다 리액트 훅으로 상태를 다룰 때 사용되지만, 각각의 사용 목적과 동작 방식에는 중요한 차이점이 있습니다.

useState
useState 훅은 컴포넌트의 상태(state)를 관리할 때 사용됩니다.
상태(state)를 선언하고, 해당 상태의 초기값과 함께 상태 업데이트 함수를 반환합니다.
컴포넌트에서 상태(state)가 변경되면 컴포넌트가 리렌더링됩니다.
상태를 업데이트하는 경우, 이전 상태를 대체하는 방식으로 동작합니다.
즉, useState로 관리되는 상태가 변경되면, 이전 상태와 새로운 상태의 차이점을 기반으로 리렌더링이 수행됩니다.
예제:

 

import React, { useState } from 'react';

const MyComponent = () => {
  const [count, setCount] = useState(0); // 초기값은 0

  const increment = () => {
    setCount(count + 1); // 이전 상태를 가져와서 1을 증가시킴
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
};

 

useRef
useRef 훅은 DOM 요소에 접근하거나 컴포넌트의 다른 값들을 유지할 때 사용됩니다.
useRef를 사용하여 생성된 ref 객체는 컴포넌트의 생명주기 동안 유지되며, 리렌더링과 관련없이 항상 동일한 값을 유지합니다.
useRef로 생성된 ref 객체의 current 프로퍼티를 사용하여 해당 값을 읽거나 수정할 수 있습니다.
useRef를 사용하여 값을 변경하더라도 컴포넌트는 다시 렌더링되지 않습니다. <- 이부분 잘못 설명함. 아이고 두야..

 

import React, { useRef } from 'react';

const MyComponent = () => {
  const countRef = useRef(0); // 초기값은 0

  const increment = () => {
    countRef.current += 1; // 리렌더링이 발생하지 않음
  };

  return (
    <div>
      <p>Count: {countRef.current}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
};

요약하자면, useState는 컴포넌트의 상태를 관리하고 상태가 변경될 때 리렌더링이 발생합니다. 반면에 useRef는 DOM 요소에 접근하거나 컴포넌트의 값을 유지하기 위해 사용되며, 리렌더링과 관련없이 항상 동일한 값을 유지합니다. 따라서 상태 업데이트로 인한 리렌더링을 필요로 하지 않는 경우에는 useRef를 사용하여 컴포넌트의 값들을 유지하는 데에 유용합니다.

 

4. 자바스크립트에서 비동기 코드를 쓸 수 있는 점을 스레드 관점에서 설명 해보세요?

 

쓰레드 관점에서의 동기와 비동기

 

멀티 쓰레드 생성의 2가지 전략인 비동기 쓰레드와 동기 쓰레드에 대해 소개해보려합니다. 비동기 쓰레드에서는 부모 쓰레드가 자식 쓰레드를 생성하고나서 자신의 실행을 이어나가는데 이것은 부모와 자식 쓰레드가 동시에 작업을 수행하며 각각의 실행 흐름이 독립적임을 의미합니다. 쓰레드들은 독립적이므로 대게 쓰레드 간의 데이터 공유가 발생하게 됩니다.

 

동기 쓰레드는 부모 쓰레드가 한개 또는 그 이상의 자식 쓰레드를 생성하고 모든 자식의 작업이 끝날때까지 대기한 후에 부모 자신의 실행이 재개됩니다. 부모에 의해 생성된 자식 쓰레드들은 동시에 작업을 수행하지만 자식 쓰레드들의 작업이 모두 완료될때까지 부모 쓰레드는 중단됩니다. 각각의 자식 쓰레드가 작업을 마치게 되면 그것들은 종료되고 그들의 부모 쓰레드로 합류(join)하게 됩니다. 일반적으로 동기 쓰레드는 쓰레드들 간의 중요한 데이터 공유를 수반합니다. 예를 들어 부모 쓰레드가 여러 자식 쓰레드에 의해 계산된 결과들을 취합해야할 수 있습니다.

 


후기

 

이상 대충 생각는 것들을 인터넷 서칭을해서 찾아 봤다...

내가 부족한 점을 많이 알 수 있는 기회였다.

특히 타입스크립트는 여기에 정리 하지 않았는데

따로 공부할 예정..

좋은 회사인거 같은데 답을 못하고 많이 틀려서 

떨어질듯.. 아쉽다.

728x90