2024-10 TIL

Soo Im·2024년 10월 1일
0

일별 TIL 기록

목록 보기
19/20
post-thumbnail

2024-10-01 <리액트>

강의 듣다 보니... => 이런 당황스러운 문법이 나와서;; 잠시 멈추고 알아봤다.

Arrow Function

Arrow function은 JS E6S에서 쓸 수 있는 함수 표현식이다.

function my_func(a){
  return a + 1;
}

var my_func = function(a){
  return a + 1;
}

var my_var = (a) => {return a + 1}

var my_var = a => return a + 1;

네 개 모두 함수를 표현한다.

Arrow Function 표현식에서 파라미터를 감싸는 괄호 ()나 return을 감싸는 중괄호 {}는 안의 내용이 단일 값일 때 생략이 가능하다.

Assigning Function

위의 예시를 보면 var my_var = function(), function 자체가 변수에 할당된 것을 볼 수 있다.
생소해 보이지만 Python에서 lambda function이랑 비슷하게 생각하면 된다. my_var = lambda a: print(a)

그럼 var my_var = function (할당 assigning)가 function my_func (선언 declaring)와 비교해 다른 점은 무엇일까?

  1. 호이스팅 제외
    JS에서는 함수를 코드 내 어디에서 선언하더라도 무조건 함수를 맨 먼저 선언하기 때문에 (hoisting = 끌어올리다), function my_func 선언보다 먼저 my_func을 호출해도 상관없다.
    하지만 변수에 assign하면 호이스팅에서 제외되어, 변수가 선언되어야 함수도 생성된다.
  2. 익명 함수 (이름 없음)
    헷갈릴 수도 있는데... 위에서 함수를 할당받은 my_var함수 이름이 아니다. my_var은 변수 이름이고, 변수에 할당된 함수는 이름이 있을 수도 있고 없을 수도 있다.
    var my_var = function () 이면 함수 이름이 없는 것이고,
    var my_var = function my_func() 이면 함수 이름이 my_func인 것이다.

Declaring 대신 Assigning을 쓰는 게 좋은 케이스는 아직 못 찾았다. 예시를 봐도 declaring에 비해 큰 이점은 없는 것 같아서... 굳이 찾자면 호이스팅 제어 정도?

공부하다보면 더 유리한 케이스를 찾겠지 🙄

2024-10-02 <리액트>

JSX (1) - 요소 만들기

이전 시간에 JS 안에서 바로 인터랙티브 요소를 만들고 HTML로 렌더링하는 것을 배웠다.

// 요소 생성
const btn = React.createElement("button", {
  id: "btn",
  onClick: ()=> console.log("I'm Clicked"),
}, "Click me");

// 렌더링
const root = document.getElementById("root");
ReactDOM.render(btn, root);

이걸 HTML과 유사한 문법으로 쓰도록 도와주는 것이 JSX 문법이다. 동작은 동일하고, 그냥 표기 방법만 달라진다.

JSX 문법으로 쓰면 문법 변환기를 통해 자동으로 위의 코드로 해석돼서 실행이 된다.

// 요소 생성 with JSX
const Btn = (<button
             id="btn"
             onClick={()=> console.log("I'm Clicked")}>
             Click me</button>);

((내가 HTML을 자주 안 써서 그런지 오히려 윗 방식이 더 편리해보이는 것 같기도...))

2024-10-09 <리액트>

JSX (2) - 요소 마트료시카

React에서 한 요소 안에 여러 요소를 넣을 때에는 createElement의 마지막 인자인 'content'에 내부 요소를 리스트로 전달해준다.

const btn = //...
const title = //...

const container = React.createElement("div", null, [title, btn])

이걸 JSX로 표현하면 똑같이 HTML 문법처럼 div안에 내부 요소를 집어 넣으면 된다.

<!--HTML-->
<div>
  <title>I'm a Title</title>
  <button>Clcik me</button>
</div>
// JSX
const Container = () => (
        <div>
            <Title />
            <Btn />
        </div>
    );
ReactDOM.render(<Container />, root);

그런데 이렇게 할 때 주의할 점이 있다.

첫째, 생성한 컴포넌트는 대문자로 시작해야 한다. (Title, Btn) 소문자로 시작하면 HTML 태그로 인식을 해버린다.

둘째, 내부 요소는 함수로 만들어야 한다. const Btn = ( <button></button>) 가 아니라

const Btn = () => ( ... );
function Btn () { return ... };

로 해주어야 한다.

div 태그 안에서 호출하는 <Title />은 요소가 아니라 함수가 되어야 한다.

<Title />은 무슨 뜻?
HTML 태그 열면서 닫는 그 태그다.

왜 함수로 만들어야 해?
그냥 const Title = (<h3>Title</h3>);로 만들고 호출하면 안되나? 싶지만...
Python에 비유하자면 변수를 만드는 것과 class를 만드는 것의 차이이다.
class로 Title을 만들면 호출될 때마다 새로운 인스턴스가 만들어지고, 인스턴스는 필요에 따라 각각 바뀔 수 있다.
하지만 변수로 Title을 만들면 고정된 값이기 때문에 무조건 하나의 값만 사용이 가능하다.
그래서 Title을 함수로 생성하는 것이다.

난 인스턴스 필요없는데...
그러면 고정된 요소로 만들고 다음과 같이 쓰면 된다.

const Container = (
  <div>
    {Title}
    {Title}
  </div>
);

2024-10-12 <리액트>

state (1)

state는 데이터에 따라 변동하는 UI를 만들 때 사용하는 도구이다.

정확한 설명은 나중에 배우고... 우선 React JS에서 state 없이 데이터를 바꾸는 단순한 방법부터 배워보자.

  • Vanilla JS
// 텍스트 넣을 span 태그 가져오기
const my_span = document.querySelector("span");

// counter를 업데이트하고 span text를 다시 쓰기
function handleClick () {
  	counter = counter + 1;
  	my_span.innerText = `Total Clicks: ${counter}`;
};

button.addEventListener("click", handleClick);
  • React JS
let counter = 0;

// counter를 업데이트하고 다시 렌더링 하기
function handleClick() {
  counter = counter + 1;
  render();
}

// 만든 요소를 렌더링하기
function render() {
  ReactDOM.render(<Container />, root);
};

// 함수가 포함된 요소 만들기
const Container = () => (
  <h3>Total Clicks: {counter}</h3> 
  <button onClick={handleClick}>Click</button>
);

// 최초 렌더링하기
render();

Vanilla JS는 span 안의 text를 직접 바꾸고,
React JS는 요소 자체는 그대로 두되 다시 렌더링하는 방식으로 진행한다.

Vanilla JS는 텍스트 전체가 다시 업데이트되는 데 반해, React JS는 counter만 업데이트가 된다. 변경되는 부분만 업데이트 하다보니 성능 상 도움이 된다.

다만 위의 예시는 요소가 바뀌는 함수마다 render()를 호출해주어야 하는 귀찮음이 있어... 다음 강의에서는 이 점을 해소한다.

2024-10-13 <리액트>

state (2)

React JS는 데이터의 변화를 감지하면 자동으로 요소를 리렌더링시키는 hook을 제공한다. 이게 state다!

  1. 어떻게 만드나?
    React.useState()로 생성한다.
    이건 [state, modifier] 배열을 반환하는데 state는 변화를 감지할 데이터, modifier은 state를 변화를 만드는 함수이다.
    위 예시에서 state는 counter, modifier은 counter = counter +1과 같다고 보면 된다.

  2. 어떻게 사용하나?
    state에 변동하는 값을 넣고, modifier에 바뀔 state 값을 전달하면 된다. 예를 들어 function(1)을 하면 state의 값은 1로 바뀐다. (보통 modifier 이름은 set_CounterName으로 한다.)
    주의할 점은 state와 modifier을 생성할 때에는 해당 state가 들어가는 요소 내부에서 생성해야 한다는 것이다.
    hook을 만들 때 그 hook이 어디에 있는지 알려주어야 해서 그렇다.

// 올바른 방법. (요소 내부에서 생성)
// 이렇게 해야 리액트가 '내가 감시할 게 'Container' 컴포넌트 안에 있구나! 라고 알 수 있다.

function Container() {
  // useState(0) -> state 초기값을 0 으로 선언한다.
  const [counter, setCounter] = React.useState(0); 
  // onClick 발생하면 counter를 +1 로 바꾼다.
  const onClick = () => {
    setCounter(counter + 1);
  };
  return (
    <div>
      <h3>Total Clicks : {counter}</h3>
      <button onClick={onClick}>Click me</button>
    </div>
  );
};
// 잘못된 방법. (요소 외부에서 생성)
// 이렇게 하면 React JS가 어느 컴포넌트를 감시해야 할 지 모른다.

const [counter, setCounter] = React.useState(0);
const onClick = () => {
  setCounter(counter + 1);
};

function Container() {       
  return (
    <div>
      <h3>Total Clicks : {counter}</h3>
      <button onClick={onClick}>Click me</button>
    </div>
  );
};
  1. 어떻게 작동하나?
    위 예시에서 modifier (setCounter)가 동작하면 (즉 state (counter)가 바뀌면) 리액트는 Container 컴포넌트를 리렌더링한다.
    즉 이전처럼 매번 함수 끝에 렌더링을 호출할 필요 없이, 바뀌는 변수와 그 함수를 hook으로 생성해서 렌더링을 자동으로 하는 것이다.

const [a, b] = [1, 2]
이렇게 하면 const a = 1, const b = 2를 한 것과 같다. JS의 이 문법을 Destructing assignment (구조 분해 할당...?)라고 한다.
위 예시에서 const [counter, setCounter] = React.useState(0);도 마찬가지로 state를 counter로, function을 setCounter로 할당한 것과 같다.

setCounter(0) 해도 리렌더링 되나?
0과 같이 고정값을 전달해서 Counter 값이 전과 동일하다 해도 리렌더링은 실행된다. 즉 값 자체의 변화 여부보다 modifier가 동작했는지가 중요하다.

2024-10-17 <리액트>

setCounter(counter + 1)로 counter에 1을 더했지만, 이건 사실 권장하는 방법은 아니다.
강의에서는 setCounter(counter => counter + 1)을 사용해야 최신의 counter 값을 쓸 수 있다고 설명한다.

이게 무슨 소리야? 뭐가 최신이라는거야? 하고 이해가 안 됐는데...
이 글을 읽으니 이해하는 데 도움이 되었다. 정리해보자.

Direct vs. Functional State Update

앞의 방식 (counter + 1)은 counter에 직접 1을 더해서 direct, 뒤의 방식은 (counter => counter + 1)은 함수를 사용해서 functional이다.

// initial value of counter = 0

// direct
function onClick1() {
setCounter(counter + 1);
setCounter(counter + 1);
setCounter(counter + 1);
}

// functional
function onClick2() {
setCounter(current => current + 1);
setCounter(current => current + 1);
setCounter(current => current + 1);

}

놀랍게도(!) 둘의 결과는 다르다. dircet는 1이 나오고, functional은 3이 나온다.

이 차이는 리액트의 비동기 처리 때문에 발생한다. (정확히는 작업을 완전히 완료하지 않고, 예약만 해 둔 다음 나중에 모아서 처리하는 것 때문에)

direct에서 우리가 counter +1을 세 번 하지만, 리액트는 계산한 결과를 곧이곧대로 counter에 저장하지 않는다. 성능을 위해서 임의의 간격 (batch)을 두고 변화를 모아놨다가 한꺼번에 업데이트를 한다.
첫 번째로 호출했을 때에는 counter는 즉시 업데이트가 되지 않고, 미래의 상태만 예약이 된다. 즉 counter는 그대로 0이고 미래의 counter의 값이 1로 '예약'만 된다. 그래서 두 번째로 호출을 했을 때에도 여전히 counter는 0이기 때문에, 또다시 counter는 1로 '예약'이 된다. 이걸 반복하기 때문에 첫 번째 함수의 결과는 1이 되는 것이다.

즉, direct 방식은 직전 단계에서 변화를 주더라도 비동기 처리 때문에 그 변화는 변수에 반영이 되지 않는다. 그래서 앞의 변화는 무시가 되고, 마지막 것만 수행이 된 것 같은 결과가 나온다.
(만약 맨 마지막에 counter + 10을 했으면 결과는 10이 나올 것이다.)

그럼 functional은 비동기가 아닌걸까? functional도 비동기이다. 다만 direct와 다르게 '예약' 된 값을 최신 값으로 본다. 즉 첫 번째 수행할 때 1을 예약하고, 두 번째 수행할 때 '예약된 1'을 가져와서 2를 예약한다. 그래서 결과가 3이 나오는 것이다.

그래서 강의에서 functional이 "최신의 counter 값을 쓰는" 방식이라고 표현한 것이다.
이런 이유로 강의에서는 direct 보다 functional을 권장하고 있다.

2024-10-20

VSCODE JSX - 태그 자동 닫힘 설정

VSCODE에서 JSX 쓸 때 html 태그가 자동으로 안 닫힐 때가 있다.

해결 방법:

  1. 에디터 우측 하단을 보면 html로 설정이 되어있는데
  2. 이걸 클릭한 다음
  3. JavaScript JSX로 바꿔주면 포매팅 잘 된다.


(혹시 이것만 했을 때 안 되면 Auto Close Tag Extension 설치해보기...)

2024-10-24 <리액트>

강의 하나를 지금 몇 번째 보는건지... 하지만 깊게 배웠죠? (제발)

Controlled Component vs. Uncontrolled Component

사용자가 input으로 입력하는 값을 실시간으로 state로 받는 상황을 가정하자.
이 때, 입력하는 값은 실시간으로 state로 저장이 되고 & input 필드 내의 값은 입력한 state 값이 계속 보여야 한다.

앞은 알겠는데 뒤는 뭔소리야? 당연히 input에는 사용자가 입력한 값이 보이지? 라고 생각했는데... 코드를 보자.

const [minutes, setMinutes] = React.useState(0);
const onChange = (event) => {
        setMinutes(event.target.value);
};
return (
  <div>
    <label htmlFor="minutes">Minutes</label>
    <input
      id="minutes"
      value={minutes} // 이걸 사용하면 Controlled, 사용하지 않으면 Uncontrolled
      onChange={onChange}
      />
    <h3>You are typing {minutes}</h3>
);

주석 처리한 value 속성을 보자. 초기값 0으로 렌더링했다가 -> 값을 입력하면 -> minutes가 바뀌고 -> 그 값이 다시 input 필드에 보인다.

그런데 사실 value 속성을 주석 처리해도 (당연하게도) input 필드에는 똑같이 내가 입력한 값이 보인다.
안 해줘도 되는데 왜 굳이 저 속성을 주는걸까?

그 답은 React가 가지는 state와, DOM이 관리하는 input을 동일하게 유지하기 위해서다. (Functional과 유사한 이 느낌...)
만약 value={minutes} 속성을 주지 않았다고 가정해보자.

  1. 사용자가 5를 입력한다. (보이는 값 =5)
  2. 5를 state에 저장한다. (state = 5)
  3. 그런데 서버에서 state를 10으로 바꿔버린다. (state = 10)
  4. 하지만 사용자는 여전히 본인이 입력한 5가 보인다. (보이는 값=5)

즉 보이는 값과 state가 일치하지 않을 수도 있다. 이럴 때 보이는 값 (input=5)을 Uncontrolled component라고 한다. input은 DOM (대충 말하면 HTML 문서 그 자체)가 관리하는 것이고, state는 리액트가 관리하는 것이기 때문에 value={minutes}로 연결해주지 않으면 둘의 불일치가 날 수 있는 것이다.

저렇게 value를 연결함으로써 input을 항상 리액트가 관리할 수 있는 Controlled Component로 만드는 것이다.

2024-10-30 <리액트>

헤매다 왠지 열받아서 적어두는 리스트

  1. html 태그의 속성이 간혹 JS 문법이랑 충돌하는 경우가 있다. 이런 경우는 JSX에서 html 속성을 다르게 주어야 한다.
    예를 들어 class는 (JS 문법에서 쓰는 것이라서) className, forhtmlFor... 이런 식.
  2. False는 못 쓴다. false만 가능. (왜 안돼...)
  3. 컴포넌트를 반환할 때에는 무조건 하나만 반환해야 한다.
    <div>Hello</div><div>world</div> 이렇게 두 개의 <div> 태그를 내보내면 에러가 난다.
    상위 <div></div> 혹은 React Fragment <></>로 감싸서 내보내야 한다.

2024-10-31 <리액트>

state로 컴포넌트 속성을 바꾸기

state를 이용해서 컴포넌트의 속성, 예를 들어 value, id, ... 심지어 콘텐츠까지 바꿀 수 있다.

아래는 분 (min)을 입력하면 시간 (hour)으로, 시간을 입력하면 분으로 변환하는 코드이다.

여기서 value={flipped ? amount : amount * 60} 문법을 보면, flipped 라는 state의 true/false 여부에 따라 input 박스에 표시할 값이 달라지는 것을 볼 수 있다.

같은 방식으로 disabled={!flipped}을 통해 input의 입력 가능 여부를 통제하고,
<button>{flipped ? "Mins to Hours" : "Hours to Mins"}</button>으로 버튼의 콘텐츠 (텍스트)도 바꿀 수도 있다.

    function App() {
      const [amount, setAmount] = React.useState(0);
      const [flipped, setFlipped] = React.useState(true);

      const reset = () => setAmount(0);

      const onChange = (event) => {
        setAmount(event.target.value);
      };

      const onFlip = () => {
        reset();
        setFlipped((flipped) => !flipped);
      };

      return (
        <>
          <div>
            <h1>Minutes and Hours Converter</h1>
            <label htmlFor="minutes">Minutes</label>
            <input
              id="minutes"
              type="number"
              value={flipped ? amount : amount * 60} // IF 문법과 동일
              onChange={onChange}
              placeholder="minutes"
              disabled={!flipped}
            />
          </div>
          <div>
            <label htmlFor="hours">Hours</label>
            <input
              id="hours"
              type="number"
              value={flipped ? Math.round(amount / 60) : amount}
              onChange={onChange}
              placeholder="hours"
              disabled={flipped}
            />
          </div>
          <div>
            <button onClick={reset}>Reset</button>
            <button onClick={onFlip}>
              {flipped ? "Mins to Hours" : "Hours to Mins"}
            </button>
          </div>
        </>
      );
    }

    const root = document.getElementById("root");
    ReactDOM.render(<App />, root);

0개의 댓글