DOM 리렌더링 정리
1. 순수 HTML + JS (Vanilla JS)
•
<input>에 글자를 입력해도 DOM 전체가 리렌더링되지 않고, 해당 <input> 요소만 내부 상태(값)를 바꿈.
•
다른 요소에는 영향을 주지 않음.
일반 HTML에서 <input>에 글자를 입력할 때는:
•
화면 전체는 리렌더링되지 않는다.
•
브라우저가 해당 <input> 요소의 내부 값만 업데이트할 뿐이고,
•
그 외의 요소나 구조는 건드리지 않는다.
2. React처럼 상태 기반 프레임워크 사용 시
•
<input>에 onChange 이벤트를 걸고, 상태(state)를 업데이트하면,
•
그 state를 참조하는 컴포넌트만 리렌더링됨.
◦
React는 가상 DOM을 비교(diffing)해서 바뀐 부분만 효율적으로 갱신함.
◦
그래서 화면 전체가 리렌더링되는 건 아님. 하지만 컴포넌트 단위의 리렌더는 발생함.
•
Vanilla JS는 직접 DOM 조작.
•
변경된 **요소만 브라우저가 재페인팅(repaint)**함.
•
React처럼 가상 DOM을 쓰는 구조가 아니므로 "리렌더링"이란 표현보다는 **"DOM 변경" + "화면 일부 업데이트"**가 더 정확함.
1. 브라우저 렌더링 과정 요약
브라우저는 다음 과정을 통해 화면을 그려:
1.
HTML 파싱 → DOM 생성
2.
CSS 파싱 → CSSOM 생성
3.
DOM + CSSOM → Render Tree
4.
Layout (위치 계산)
5.
Painting (픽셀 그리기)
6.
Compositing (화면에 출력)
2. Repaint vs Reflow
용어 | 설명 | 예시 |
Repaint | 색, 스타일만 바뀜 | color, background 바꿀 때 |
Reflow (Layout) | 요소 위치/크기 바뀌면 전체 레이아웃 재계산 | height, font-size 등 변경 |
•
repaint는 비용이 가장 낮고, reflow는 상대적으로 무거움
•
Vanilla JS에서 한 부분만 바꾸면 전체 repaint/reflow는 거의 없음
•
하지만 DOM 조작이 많아지거나, DOM 트리 전체에 영향 가는 조작을 하면 크고 느린 작업이 생김
React의 가상 DOM은 아래 상황을 최적화하기 위한 도구야:
•
DOM 변경이 빈번하고 복잡할 때,
•
실제 DOM에 바로 접근하지 않고, 메모리에 있는 가상 DOM에서 먼저 변경 사항을 계산
•
→ 변경된 부분만 실제 DOM에 반영 (diff 알고리즘 사용)
1. DOM 변경에 따른 렌더링 용어 정리
용어 | 발생 조건 | 설명 | 예시 |
Repaint | 시각 스타일만 바뀔 때 | 색, 배경 등만 다시 그림 | element.style.color = "red" |
Reflow (Layout) | 요소의 위치, 크기 변경 | 레이아웃 전체/일부를 다시 계산 | element.style.width = "200px" |
Composite | 레이어 조합 | GPU 레벨에서 조합 | transform, opacity 등 |
(전체) 리렌더링 | 브라우저에서는 잘 안 쓰는 개념 | 전체 DOM을 다시 만들거나, 전체 구조가 바뀔 때 | document.body.innerHTML = "<div>...</div>" |
innerText는 구조변화는 없지만, 내용 변경에 따른 reflow가 발생할 수도 있어 (글자가 길어져서 layout 바뀌면).
color, background처럼 단순 시각 변경은 repaint만 발생.
2. React에서의 "리렌더링" 개념
React에서 말하는 "리렌더링"은 브라우저의 paint/reflow가 아님.
•
React 내부적으로는 상태(state)가 바뀌면:
1.
컴포넌트 함수가 다시 실행됨 → JSX → Virtual DOM 생성
2.
이전 Virtual DOM과 diff 비교
3.
바뀐 부분만 실제 DOM에 반영
4.
실제 DOM 변경에 따라 브라우저가 repaint/reflow를 판단해서 실행
즉,
React의 리렌더링 = 컴포넌트 함수가 다시 실행되는 논리적 단계 (렌더 함수)
브라우저의 리렌더링 = 실제 화면에 뭔가 그려지거나 재배치되는 물리적 단계
•
innerText, 색 변경 → Repaint (경우에 따라 Reflow)
•
width, height, 글자 수 변화 → Reflow
•
React는 리플로우 자체를 줄이기보다는, 그에 앞선 DOM 변경 자체를 최소화하려고 함
"리렌더링"이라는 개념은 React 같은 프레임워크의 내부 개념 (컴포넌트 재실행)
React의 장점은 이런 걸 자동으로 batching하거나, diff 알고리즘으로 최소 변경만 반영해주는 데 있음.
1. 브라우저의 역할은 결과 처리일 뿐이다
브라우저는:
•
“어느 요소가 바뀌었는지”는 개발자나 프레임워크가 알려줘야 함
•
그럼 브라우저는 그 바뀐 부분만 repaint/reflow
즉, 브라우저는:
“이 div가 바뀌었어? 알았어, 거기만 다시 그릴게”
하지만 그 “뭐가 바뀌었는지 판단”은 안 해줌
2. Vanilla JS의 문제: 직접 DOM 조작
Vanilla JS에선 상태가 바뀌면 보통:
js
Copy code
document.getElementById("count").innerText = count;
Plain Text
복사
•
여기서 count가 변했는지, 그대로인지 개발자가 직접 관리해야 함
•
실수하면 불필요하게 DOM을 조작 → 브라우저는 그대로 반응(reflow/repaint) → 성능 낭비
3. React의 diffing: “변화를 판단하는 자동화”
React는 상태가 바뀌면:
•
render()를 다시 실행해서 새로운 Virtual DOM 생성
•
이전 Virtual DOM과 자동으로 비교 (diffing)
•
정말 바뀐 부분만 실제 DOM에 반영
예를 들어:
jsx
Copy code
<div>
<span>{count}</span>
<span>{name}</span>
</div>
JavaScript
복사
•
count만 바뀌면 name은 건드리지 않음
•
하지만 JSX는 매번 다 만들어지므로, 차이점을 알아내야만 실제 DOM 최적화 가능
•
→ 이게 diffing의 역할
4. 왜 diffing이 중요한가?
상태가 바뀌는 빈도 + 규모가 커질수록 “어디가 바뀌었는가”를 자동으로 추적하는 것이 개발자 입장에서 너무 중요해짐
•
작은 앱: 직접 DOM 조작해도 무방
•
대규모 SPA: 수백~수천 개의 노드, 다양한 상태
◦
수동 DOM 관리 = 유지보수 지옥
◦
→ React는 이걸 자동화해서 생산성과 성능을 모두 보장
•
브라우저는 "뭐가 바뀌었는지"는 몰라, 그냥 "바뀌었다"는 요청에 반응할 뿐
•
React의 diffing은 "정말 바뀐 부분만 DOM에 반영하는 과정"
•
즉, diffing은 브라우저가 아니라 개발자 대신 '무엇을 바꿔야 하는가'를 판단하는 역할
•
브라우저는 그걸 받아서 최적의 방식으로 repaint/reflow만 처리
React는 상태(state)가 바뀌면
그걸 감지해서
→새로운 Virtual DOM을 만들고 (declarative 방식)
→diffing을 통해 실제 DOM에 필요한 최소한의 조작만 반영하고
→그 다음은 브라우저가 알아서 repaint/reflow 처리함.
React의 핵심 역할
•
무엇이 바뀌었는지를 자동으로 판단
•
직접 DOM을 조작하지 않고 선언형으로 UI를 표현할 수 있게 함
•
개발자가 **상태(state)**만 관리하면 UI는 알아서 "어떻게 보여야 하는가"를 맞춰줌
UI = f(state) 라는 선언형 모델을 직접 구현 가능한 프레임워크
즉, 구조적으로, 코드 스타일적으로 상태 기반의 선언형 프로그래밍을 현실에서 쓸 수 있게 만든 도구임.
React는 효율적인 UI 갱신을 위해 Virtual DOM + diffing을 사용하고,
브라우저는 그 변경에 따라 필요한 만큼만 repaint/reflow한다.
→ React는 구조화와 선언형 프로그래밍을 위한 프레임워크이자 추상화 레이어다.
틀린것들
•
Vanilla JS에서 DOM이 바뀌면 전체 화면을 다시 렌더링한다
•
그래서 Virtual DOM이 필요한 거다
•
렌더링이 다시 일어나면 let 변수들도 초기화된다
•
그래서 React의 state를 써야 한다
정답
브라우저는 dom이 바뀌면 해당 요소만 reflow/repaint한다. reflow/repaint를 최적화하는것이 아니라 react는
DOM조작이 전체 페이지를 다시 그리는게 아니고 실제로는 브라우저 렌더링 엔진이 변화된 최소영역만 계산해서 그린다.
virtual dom을 사용하는 이유는 화면 전체가 다시 그려지기 때문이 아니고, state가 변경되었을때 바뀌어야 할 부분을 알아내서 = 자동으로 diff를 계산해서 필요한 부분만 수정한 복제본을 만들고 그걸로 dom에 변경사항을 적용하는것임. 즉 dom에서 state가 변경되면 변경되어야 할 부분이 어디인가? 를 알아내는 것에 목적이 있다. 그것을 선언형으로 하는것이 목적이다. 그러므로 vanilajs보다 성능은 나빠지는거다. state변경으로 인한 리렌더링을 할거니까.. reflow/repaint를 줄일수는 없다 batching으로 여러번 할걸 한번에 하니까 좋아질 수 있지만! 일반적으로 크지 않다
잘못된 오해
: Virtual DOM이 브라우저의 전체 렌더링을 막아준다
→ 실제로는 "어떤 DOM을 바꿀지 자동화해주는 중간 레이어"
실제 DOM을 직접 조작하지 않고, 선언형으로 쓰면
→ 내부적으로 Virtual DOM이 자동으로 diff를 계산
해서 필요한 부분만 수정하는 것
"상태 변화 시 어떤 부분을 바꿔야 할지 자동 판단하기 위한 구조"
틀린 이유 ③: "let 변수들은 리렌더링 시 초기화된다"
•
이건 React 개념을 잘못 끌어온 것.
•
Vanilla JS는 컴포넌트, 리렌더링, 상태 추적 개념이 없음.
•
단순히 전역 let 변수를 선언하면 그대로 유지되고,
→ DOM이 바뀌거나 화면이 다시 그려져도 자동으로 초기화되지 않음.
•
React에서 let 변수가 컴포넌트 내부에 있으면,
→ 컴포넌트가 리렌더링될 때 함수가 다시 실행되므로 초기화처럼 보임
→ 그래서 useState 같은 hook이 필요한 것
vanilajs에서 리프레시가 아니라 부분 reflow/repaint가 일어나도 변수가 초기화되지는 않음. 전역변수이기 때문. 컴포넌트에서 사용하는 let 등은 지역변수라서 초기화되니까.. state변경에 따라서 ui가 변경되도록 state를 사용하는것
1. 브라우저의 렌더링(reflow/repaint) vs React의 리렌더링(함수 재실행)
•
브라우저 렌더링: DOM의 실제 변화에 반응해 시각적으로 업데이트
•
React 리렌더링: 상태 변화에 따라 컴포넌트 함수가 다시 실행됨
→ 이 둘은 서로 다른 층위의 개념임
2. DOM 변경 = 화면 전체 초기화?
•
아니다. DOM은 참조 가능한 객체 그래프임
•
특정 노드만 바꿀 수 있고, 메모리도 그대로 유지됨
결론
브라우저는 DOM이 변경되면 그 변경된 부분만 다시 계산해서 반영한다. 하지만 직접 DOM을 조작하면 무엇이 바뀌었는지를 개발자가 직접 관리해야 해서 복잡한 UI에선 실수가 많고, 비효율적일 수 있다. 그래서 React는 상태가 바뀌면 전체 UI를 선언형으로 다시 기술하고, 내부적으로는 Virtual DOM을 통해 정확히 바뀐 부분만 DOM에 반영한다. 이때 컴포넌트 함수는 다시 실행되므로 함수 안의 변수들은 초기화되고, 유지되어야 할 값은 useState나 useRef로 관리한다.
5/10
Docker 컨테이너를 업데이트하는 일반적인 단계는 다음과 같습니다. 현재 docker-compose.yml 파일을 사용하고 있으므로, 이를 기준으로 설명하겠습니다.
1.
실행 중인 컨테이너 중지 및 제거 (선택 사항이지만 권장): 기존에 실행 중인 컨테이너가 있다면, 먼저 중지하고 제거하는 것이 좋습니다. 이렇게 하면 포트 충돌을 방지하고 완전히 새로운 환경에서 시작할 수 있습니다. 터미널에서 docker-compose.yml 파일이 있는 디렉토리(directory_scanner)로 이동한 후 다음 명령을 실행합니다.
docker-compose down
이 명령은 docker-compose.yml에 정의된 서비스의 컨테이너들을 중지하고 제거합니다.
2.
Docker 이미지 재빌드: 코드 변경 사항을 반영하여 Docker 이미지를 다시 빌드합니다.
docker-compose build
이 명령은 docker-compose.yml에 정의된 서비스 중 build 컨텍스트가 있는 서비스들(현재 frontend 및 backend)의 이미지를 다시 빌드합니다. Docker는 변경된 레이어만 다시 빌드하려고 시도하므로, 코드 변경이 감지되면 해당 부분이 재빌드됩니다.
3.
새로운 컨테이너 실행: 업데이트된 이미지로 새로운 컨테이너를 시작합니다.
docker-compose up -d
•
d 플래그는 컨테이너를 백그라운드에서 실행합니다. 만약 로그를 직접 보고 싶다면 d 플래그 없이 실행할 수 있습니다.
docker-compose up
요약: 터미널에서 directory_scanner 디렉토리로 이동한 후, 다음 두 명령을 순서대로 실행하면 됩니다.
docker-compose down
docker-compose up --build -d
•
-build 옵션을 up 명령어에 함께 사용하면, 이미지를 재빌드한 후 컨테이너를 시작합니다. d는 백그라운드 실행 옵션입니다.
이 과정을 통해 코드 변경 사항이 Docker 컨테이너에 반영되어 실행됩니다.