내배캠 본캠프

내일배움캠프 - 1/8 TIL(State, 렌더링, DOM)

장해진 2025. 1. 8. 21:59
오늘의 체크리스트는?!
오전: 리액트 입문 강의 완강(State, 렌더링, DOM, 스타일링, refactoring) - 이해하려 하지말고 일단 보기
✅ 오후: 피그마 복습(유튜브 3시간 강의)
✅ 저녁: UX집중반(4-3-3으로 나온 아이디어 인사이트로 정리, 아이디어 스케치, 사용자 니즈 고려한 개선점 채택)

 

다시 새로운 마음으로..~아좌좌! 나중에 내가 뭐했는지 회고하기 위해 TIL에 최대한 담기!
더보기

State란? 

컴포넌트 내부에서 바뀔 수 있는 값. 왜 바뀌어야 하는가? -> 목적은 하나! UI(앨리먼트)로의 반영을 위해서 

import React, { useState } from 'react';

function GrandFather() {
  const [name, setName] = useState("김할아"); // 이것이 state!
  return <Mother grandFatherName={name} />;
}

// .. 중략

 const name = "김할아"에서 const[name,setName] = useState("김할아")로 바뀐 것을 확인할 수 있음! 

useState는 리액트에서만 제공하는 기능(훅!!이라고 표현하자)이다.

 

<state 정의하기>

먼저 const로 선언을 하고 [ ] 빈 배열을 생성한다. 배열의 첫 번째 자리에는 state의 이름, 두번째 자리에는 set을 붙이고 state의 이름을 붙인다. 그리고 useState()의 인자에는 state의 원하는 처음값을 넣어준다. 

*이 처음값을 initial state라고 부른다! 

 

<state 변경하기>

1. child 컴포넌트에 할아버지 이름 바꾸기라는 라벨을 가진 버튼을 만든다.

2. 그리고 이 버튼을 눌렀을 때(onClick), setName을 실행할 수 있도록 준비한다.

3. GrandFather에 있는 setName을  Child에게 전달해줘야함 주고 받고, 주고 받고해서 전달해줌!

하지만, 이렇게 바뀐 값은 브라우저를 새로 고침하면 다시 초기값으로 바뀐다. setName을 통해서 바꾼 값은 어디에 저장되는 것이 아니기 때문에 단순히 화면에서 바뀐 값으로 다시 렌더링이 되는 것!

// src/App.js

import React, { useState } from "react";

function Child(props) {
  return (
    <div>
      <button
        onClick={() => {
          props.setName("박할아");// 받은 setName을 실행한다!
        }}
      >
        할아버지 이름 바꾸기
      </button>
      <div>{props.grandFatherName}</div>
    </div>
  );
}

function Mother(props) {
  return (
    <Child grandFatherName={props.grandFatherName} setName={props.setName} />
  );
}

function GrandFather() {
  const [name, setName] = useState("김할아");
  return <Mother grandFatherName={name} setName={setName} />;//주고~
} 

function App() {
  return <GrandFather />;
}

export default App;

 

<State 기본 응용>

(1) 버튼과 이밴트 핸들러 구현

버튼을 눌렀을 때 하고 싶은 행동을 함수로 만들 수 있음! 

HOW? onClickHandler라는 함수를 만들고 onClick={}에 넣어줌 

= 이런 함수를 이벤트 핸들러라고 표현한다. 

⬇️

console.log("hello button")코드를 작성하고 버튼을 누른 후 콘솔을 확인해 보면, 

우리가 만든 함수로 인해 버튼을 누를 때마다 hello button이 찍히는 것을 확인 가능

import React from "react";

function App() {
  // 버튼을 눌렀을 때 하고 싶은 행동 
  function onClickHandler() {
    console.log("hello button");
  }
  return (
    <div>
      <button onClick={onClickHandler}>버튼</button>
    </div>
  );
}

export default App;

(2) state 구현하고 이벤트 핸들러와 연결하기 

(2-1) UseState + onClick event

state를 하나 만든다. 그리고 이 버튼을 클릭했을 때, state값을 바꿔보기 

1. 이벤트 핸들러를 만들어주고 그 안에 setName을 넣어줌 

2. setName안에 있는 값이 "누렁이"이므로, state가 "길동이"에서 "누렁이"로 바뀜!

import React, { useState } from "react";

function App() {
  const [name, setName] = useState("길동이");

  function onClickHandler() {
    setName("누렁이");
  }

  return (
    <div>
      {name}
      <button onClick={onClickHandler}>버튼</button>
    </div>
  );
}

export default App;

(2-2) useState + onChange event

input과 useState를 이용해 input의 값을 넣을 value라는 state를 생성.

// 	input과 state 구현하기
import React, { useState } from "react";

const App = () => {
  const [value, setValue] = useState("");

  return (
    <div>
      <input type="text" />
    </div>
  );
};

export default App;

 

input과 생성한 state를 연결! 

먼저 input에 onChange라는 이벤트를 불러내고, 우리가 생성한 이벤트핸들러 함수를 넣는다. 

우리는 이제 핸들러 안에서 자바스크립트의 event 객체를 꺼내 사용할 수 있다. 사용자가 입력한 input의 값은 event,target,value로 꺼내 사용할 수 있음! 

마지막으로 state인 value를 input의 attribute인 value에 넣어주면 input과 state 연결 성공!

//이벤트 핸들러를 구현하고 state와 연결하기

import React, { useState } from "react";

const App = () => {
  const [value, setValue] = useState("");

  const onChangeHandler = (event) => {
    const inputValue = event.target.value;
    setValue(inputValue);
    console.log(inputValue);
    
  };

	console.log(value) // value가 어떻게 변하는지 한번 콘솔로 볼까요?

  return (
    <div>
      <input type="text" onChange={onChangeHandler} value={value} />
    </div>
  );
};

export default App;

 

불변성이란?

메모리에 있는 값을 변경할 수 없는 것! 

<변경 가능한 방법>

let numbers = [1, 2, 3];
numbers.push(4); // 배열에 직접 요소를 추가
console.log(numbers); // [1, 2, 3, 4]

이게 왜 문제일까? - number.push(4)를 사용하면 이 작업이 배열 numbers의 불변성을 깨뜨리기 때문! 
ex) 만약 1,2,3,4에서 4를 5로 바꾸면 그래도 네개이긴 함. 근데 나중에 하나를 더 추가하면 1,2,3,5,5가 될 수 있음 

<불변성을 유지하는 방법>

let numbers = [1, 2, 3];
let newNumbers = [...numbers, 4]; // 새 배열을 생성하여 기존 배열을 변경하지 않음
console.log(numbers); // [1, 2, 3]
console.log(newNumbers); // [1, 2, 3, 4]

<변수를 저장하면 메모리에 어떻게 저장이 되는가?>

let number = 1이라고 하면 메모리에는 1이란 값이 저장됨 그리고 Number라는 변수는 메모리에 있는 1을 참조함! 

이어서 우리가 let secondNumber = 1 이라고 다른 변수를 선언했다고 가정해보면, 

이 때도 메모리에 생성되어 있는 1을 참조함! 즉, number와 sedcondNumber는 변수의 이름이 다르지만, 같은 메모리의 값을 바라보고 있는 것. 그래서 우리가 Number === secondNumber를 하면 true라고 보임! 

 

하지만 원시데이터가 아닌 값(객체, 배열, 함수)는 이렇지 않다. 

let obj_1 = {name : "kim"}이 라는 값을 선언하면 obj_1이 저장됨. 

let obj_2 = {name : "kim"}라는 값을 선언하면, obj_2라는 메모리 공간에 새롭게 저장됨. 

그래서 obj_1 === obj_2가 false임

<데이터를 수정하면 어떻게 될까?>

기존에 1이던 number를 number = 2 라고 새로운 값을 할당하면 메모리에선 어떻게 되나?

원시데이터는 불변성이 있어서, 즉 기존 메모리에 저장되어 있는 1이라는 값은 변하지 않고 새로운 메모리 저장공간에 2가 생기고 number라는 값은 새로운 메모리 공간에 저장된 2를 참조하게 됨! 

즉, secondNumber를 콘솔에 찍으면 여전히 1이라고 콘솔에 보임. number와 secondNumber가 각각 다른 메모리 저장 공간을 참조하고 있기 때문이다!

 

obj_1을 수정해보자! 
obj_1.name = "park"라는 새로운 값을 할당하면? 객체는 불변성이 없으니까 {name: "kim"}이 {name:"park"}으로 바뀜

 

<리액트에서 불변성이 가지는 의의>

리액트에서는 화면을 리렌더링 할지 말지 결정할 때 state의 변화를 확인함. state가 변했다면 리렌더링 하는 것이고, state가 변하지 않았다면? 리렌더링을 하지 않겠쥐..

 

그 때, state가 변했는지 변하지 않았는지 확인하는 방법이 state 변화 전, 후의 메모리 주소를 비교하는 것! 

그래서 만약 리액트에서 원시데이터가 아닌 데이터를 수정할 때 불변성을 지켜주지 않고, 직접 수정을 가하면 값은 바뀌었어도 메모리 주소가 변함이 없음. 

그러면 마땅히 일어나야 할 리렌더링이 일어나지 않게 됨..

 

<리액트 불변성 지키기 예시>

import React, { useState } from "react";

function App() {
  const [dogs, setDogs] = useState(["말티즈"]);

  function onClickHandler() {
		// spread operator(전개 연산자)를 이용해서 dogs를 복사합니다. 
	  // 그리고 나서 항목을 추가합니다.
    setDogs([...dogs, "시고르자브르종"]);
  }

  console.log(dogs);
  return (
    <div>
      <button onClick={onClickHandler}>버튼</button>
    </div>
  );
}

export default App;

 

더보기

DOM?

리액트나 뷰는 가상돔을 사용해서 원하는 화면을 브라우저에 그려줌. 자체적으로 상당히 효율적인 알고리즘을 사용해서 그려주기 때문에 속도가 빠름!

<그래서 DOM이란?>

수 많은 컴포넌트로 구성된 웹페이지들을 보게 됨. 이 페이지를 문서라고 하고, 페이지를 이루는 컴포넌트는 엘리먼트라고 함.

DOM은 이 엘리먼트를 tree 형태(DOM TREE)로 표현한 것! 

트리 요소 하나하나를 '노드'라고 부르고 각각의 노드는 해당 노드에 접근과 제어를 조작할 수 있는 API를 제공 

*API : html 요소에 접근해서 수정할 수 있는 함수

// id가 demo인 녀석을 찾아, 'Hello World!'를 대입해줘.
document.getElementById("demo").innerHTML = "Hello World!";

// p 태그들을 모두 가져와서 element 변수에 저장해줘
const element = document.getElementsByTagName("p");

// 클래스 이름이 intro인 모든 요소를 가져와서 x 변수에 저장해줘
const x = document.getElementsByClassName("intro");

 

<Virtual DOM>

리액트는 가상DOM을 이용해서 실제 DOM을 변경하는 작업을 수행 

실제 DOM을 조작하는 것보다, 객체 형태로 메모리에 저장되기 때문에 훨씬 더 빠르게 조작을 수행할 수 있음

1. 가상 DOM 생성

인스타그램의 하트가 빨강으로 채워지기 위해선 빨간 하트에 해당되는 엘리먼트 DOM요소가 갱신되어야 함. 

이 과정에서 리액트는 화면이 갱신되기 전 구조가 담겨있는 가상 DOM 객체 / 화면 갱신 후 보여야 할 가상 DOM 객체를 가지고 있음 리액트는 state가 변경되어야만 리렌더링이 됨! 바로 두번째에 해당되는 가상 DOM을 만드는 것

2.Diffing

state가 변경되면 2번에서 생성된 가상돔과 1번에서 이미 갖고 있던 가상돔을 비교해서 어느 부분에서 변화가 일어났는지 빠르게 파악함.

3. 배치 업데이트

파악이 끝나면, 변경이 일어난 그 부분만 실제 DOM에 적용 시켜줌. 적용시킬 때는, 한 번에 적용(Batch Update)

 


아이콘 그리기

https://youtu.be/c6yCZecrMpQ?si=HyVpq_XDZhoYjf2x

이 강의 듣고 피그마 기초 한 번 다시 잡고..! 

아이콘 그리기 강의를 들었다! 

materil design 아이콘 따라 그리기해봤는데, 펜툴 다루는게 어색해서 이것보다 어려운 아이콘은 어떻게 해야할지 더 레퍼런스를 찾아봐야할 것 같다. 

내일부터 금요일까진 아이콘 그리기 + 컴포넌트 만들기를 하면서 ui적으로 보충할 시간을 가질 예정 

 


4-3-3 brainwriting을 통해서 핵심 문제에 대한 개선점 아이디어를 각자 적은 뒤, 그 아이디어에 더 보충할 아이디어를 덧붙였다. 

그 후에 개선 효과가 좋고, 개선 가능한 아이디어를 위주로 별을 매긴 후.. 

글로 정리해봤다. 

총 4개의 개선점을 적은 후, 개선했을 때 예상되는 문제점과 기대효과와 긴급성을 별점으로 매겼다. 

그리고 각 개선점이 voc와 설문조사에서 어떻게 나온 문제점인지도 옆에 정리해봤더니 결제하기 댑스에 대한 불만, 중복 내용이 많아 중요 내용을 찾기 어렵다는 의견이 많았다. 

 

🚨 근데 여기서 어려웠던 것! 그래서 이렇게 개선점들을 통해 화면을 구상했을 때, 전체적으로 댑스를 삭제해서 화면에 변화가 큰 개선안1과 표면적인 문제를 구성만 다르게 해서 배열한 개선안2 중에 어떤게 적합한가? 

모르겠어서 튜터님께 방문했다.

 

<튜터님의 답변 정리>

  • 문제 정의 단계에서 그 문제가 왜 문제인지 정의하는 것에 따라 솔루션의 방향이 크게 달라진다.
  • 우리가 '문제'라고 정의하여 솔루션으로 제안한 것이 공급자 입장에선 왜 이렇게 구상했는지를 생각해보아야 한다. 
  • 광고, 혜택 등이 불필요해 보일지라도 공급자 입장에선 브랜드적 가치, 사용자에게 제공하는 로열티를 보여주는 것, 사용자 친화적으로 ux를 개선해야 하는 것도 맞지만, 공급자 입장도 생각해봐야 함! 
  • 솔루션 단계에서의 UT는 개선안이 모두 다 나오고 디자인까지 됐을 때, ab테스트 등을 진행해야 한다. 개선안이 그만큼 나왔다는건 우리가 리소스를 투자한다는 것. 아이디어를 구체화하고, 와이어 프레임도 그리고 기능을 구현하고 디자인까지 해야 a/b test로 인사이트를 얻은게 의미가 있어지는 것 
  • Q.(내 질문!) 문제 정의를 할 때, VOC와 설문조사 밖에 소스가 없다면 예를 들어 부가기능이 보이지 않아요, 결제 내역이 어디 있는거야? 라는 VoC만 있는데 이 것을 문제로 정의하기 위해서 우리의 주관이 들어갈 수 밖에 없는 것 같은데 이건 괜찮은가요? 
    A. 모든~ 단계에서 UT를 진행할 수는 없어요. 그러려면 매일 매일 사용자를 만나야 합니다. 즉, 결제 내역이 어디있는거야?에서 5whys를 통해 디자인적 요소를 1차적으로 분석해보고 ux적인 요소도 분석해보고 위계도 분석해보는 것. 이 과정에서 100% 주관성을 배제할 순 없어요. 이 때 아이데이션 과정을 거쳐 아이디어가 나온다고 이해하면 됩니다. 

    예시로, 가시성이 떨어지네? 왜 떨어질까 백그라운드를 주지 않으니까 안 보이네. 이 일러스트 타입이 얘네가 원래 쓰는 일러스트 디자인 가이드인건 알겠는데 밑에처럼 3D를 쓰지 않은 이유는 뭘까? - 다른 부분에선 어떻게 쓰지? - 아, 메뉴에서는 클래식한 아이콘을 쓰는구나 ->하면 우리가 디자인 가이드를 흔들 수는 없어요! 
    그래서 사용성 테스트를 통해 멤버십을 적용해보세요. 하는 테스크를 줘서 어떤 부분에서 못찾는지 알아내는 과정을 겪는 것.
  • Q. (내 질문)개선안 A, B의 차이는 필요한 부분만 고치거나, 대대적으로 리뉴얼한 것의 차이가 있는데 이 부분에서 우려될만한 점은 없나요? 
    A. 잘 만든 앱이라면 아주 작은 것만 개선해도 될텐데, 획기적으로 뭔갈 많이 줄이고 솔루션이 나온단건 문제가 많다는 뜻일 것.
    하지만 회사라고 생각하면, 비단 화면만 개선하는게 아니라 개발적인 부분도 건드려야 함. 그래서 이렇게 개선이 많이 된 경우엔 리뉴얼로 봐야 하긴 해서 a/b 테스트로 해결되진 않는다! a/b 테스트는 변경된 일부 한 두 곳만을 딱 비교해서 뭐가 나은지를 채택할 수 있어야 하는 방법. 
    강력한 근거가 있어야함! 강력한 근거란? 결제로 이어지지 않는 근거나 데이터가 있어야함. 
    그래서 화면의 댑스를 줄이는 것보다는 폰을 흔들면 결제 바코드가 나온다거나 하는 솔루션도 있을 수 있음. 하지만 주의해야할 점! 
    문제 정의 단계에서 댑스가 많아 결제 과정이 불편하다.가 결론이라면 바코드를 흔들어서 바코드가 나오게 했다는 솔루션 ok 하지만, VoC에서 단순히 결제 과정이 복잡하다해서 해결한건 VoC해결이다. 

👍 잘한 점

  • 튜터님께 방문하기 전에 우리가 궁금한 것을 딱 정리해 가야한다는 것을 매~~번 느끼고 있다. 그래야 더 좋은 인사이트를 많이 얻어오고, 튜터님께서도 우리의 진행과정을 잘 이해하실 수 있는 것 같다. 오늘 9시가 넘어서(죄송..) 피드백을 듣고 왔는데 이해가 쏙~~돼서 속이 후련해졌다....
  • 리액트 강의는 그래도 끝까지 완주했다!! 막연했는데 알고 보니 마지막 두 개 강의가 예시를 통째로 들어주는거여서 이걸 보고 이번주 발제를 들었다면 더 잘 이해가 됐을텐데..라는 생각이 들었다. ㅎ

😦 아쉬운 점 

  • 오늘은 열심히 했다!!!

앞으로 해야할

  • 내일도 아이콘 만들기 + 컴포넌트 만들기 + 디자인 카타 다시 한 번씩 보기
  • UX 개선안 2개 중에 솔루션으로 어떤 것 할지 정하고 한 문장으로 정의해보기 + 장표 만들기 
  • 최종프로젝트 레퍼런스 찾아보고, 포트폴리오 글 정리하기