티스토리 뷰
최근에 HTMLElement에 대해 기초단계에서 자세하게 알려주진 않는것 같다. 하지만, 공부를하면, 기초가 중요하다는 것을 알게되는 것 같다. react를 배우면 가상돔을 다루지만, ref과 같은 깊은 지식으로 들어갈 수록 기초의 부재는 빠른 성장을 막는다. 또한 typescript로 적용을 하다보면 상속 시 HTMLElement 에 관련된 이야기들이 많이 나온다. 오늘은 HTMLElement에 대해 이야기를 풀어볼까 한다. 깊게 들어가면 끝도 없다. 따라서 이 글을 통해 맛보기로 기초개념을 이해한 후에, 필요하다면 관련 내용을 조금 더 공부해 보기를 추천한다.
간단한 예를 추가하자면, 우리가 javascript에서 사용하는 appendChild는 Node가 제공하는 method이다. appendChild는 Node 에서 상속받아서 사용되는 것이라고 할 수 있다. 간단히 예를 들면 아래와 같다.
let test = document.createElement('p');
test.innerTest = 'test'
document.body.appendChild(test)
위의 순수함수와 다르게 react에서는 아래와 같은 순서로 변환을 한다. 18 버전 기준으로 코드는 아래와 같다.
import React from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
reportWebVitals();
- ReactDom.createRoot는 리액스트 실행하는 root div 태그에 연동을 한다.
- React.createElement는 HTML 요소를 가상 DOM 객체로 구현한다.
- .render 부분은 가상 DOM 객체를 물리 DOM 객체로 변환한다.
가상 DOM은 기본적으로 메모리를 사용하기 때문에 빠르다. 그리고 변경 전 DOM과 현재 DOM을 비교하여 변경된 부분의 DOM 만 업데이트 한 후에 render된다. 따라서 부분의 요소가 변경 시 전체를 변경해야하는 물리 DOM에 비해 효율적이라고 할 수 있다.
0. element 란
element란 무엇일까? element를 구성하고 있는 tag는 <a>, <p>, <div> 같은 것들을 태그라고 한다. 이러한 태그들은 <a></a>, <p></p>, <div></div> 와 같이 시작태그와 종료태그로 나누어 진다. 시작태그는 <input type="text" /> 과 같이 속성과(attribute) 값(value)을 가질 수 있다. element는 이러한 시작태그와 종료태그가 모두 포함된 것을 말한다. 조금 더 나아가면 html은 이러한 element들로 구성되어 있다.
HTMLElement 인터페이스란 무엇일까? 인터페이스 자체는 객체가 제공해야 할 여러 기능을 정의한 규약이라고 할 수 있다. 웹 브라우저는 document.createElement 메서드를 통해서 목적에 맞게 객체를 구현 가능하다.
1. HTMLElement
Dom tree에 대한 간단한 알고 가야할 시기가 왔다. 문서 객체 모델(DOM; Document Object Model)은 말그대로 객체로 element를 보는 것이다. 아래의 간단한 html 코드를 보자.
<!DOCTYPE HTML>
<html>
<head>
<title></title>
</head>
<body>
<div></div>
</body>
</html>
위를 tree 구조로 보면, html 태그 내부에 head, body 태그가 들어가고, body 내부에 div가 포함되는 것을 알 수 있다. 간소하게 말하자면, 이게 바로 DOM트리이다. 기본적은 html은 html 태그 내부에 다 포함되는 것을 알 수 있다.
2. typescript 적용
typescript를 사용해서 조금 더 관련 이해도를 높여보자. 예를 들어 아래와 같은 Button 컴포넌트를 만들었다고 하자.
interface ButtonProps {
children: string;
size: "lg" | "md" | "sm" | "xs";
onClick: () => void;
}
const Button = ({children, size, onClick}: ButtonProps) => {
return (
<button onClinck={onClick}>
{children}
</button>
)
}
export default Button
위의 내용을 보면, interface로 전달 받을 props를 정해해서 받은 것을 알 수 있다. 위의 컴포넌트는 아래와 같이 사용하기가 가능하다.
import Button from "Button";
const App = () => {
return <Button onClick={() => console.log("Button event~")}>Button</Button>;
};
export default App;
잘 돌아가긴한다. 그러나 고민을 해봐야할 부분은, button 컴포넌트를 만들 때, html button의 기본 규약을 지키면서 만드는 것이 당연히 좋다. 그리고, 기존 button 에 포함된 onClick 같은 것들은 만들지 말고 기존 것을 쓰는 것이 더 좋다. 즉, 여기서는 interface로 새롭게 만드는 것이 아니라, 기존의 HTMLButtonElement를 상속 받아서 버튼 컴포넌트를 만드는 것이 당연히 좋다. 예를들면 원래 버튼이 가지고 있는, disabled authfocus 같은 것들을 원래의 것을 사용하기 위해서는 HTMLButtonElement를 상속 받는 것이 당연히 좋다. 이러한 내용을 반영하면 아래와 같다.
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {}
const Button = (props: ButtonProps) => {
return <button {...props} />;
};
export default Button;
HTMLButtonElement는 무엇일까? 위에서 이야기한 HTMLElement의 하위 개념이다. HTMLElement 하위에는 HTMLButtonElement 뿐만 아니라 HTMLDivElement를 포함한 여러 태그 element들이 포함된다. 확장하면, 각각의 커스텀 태그를 만들때, 기본 HTMLElement 내부에 있는 표준으로 상속 받아서 사용한다면, 기존 태그의 속성들을 유지하면서 커스텀 컴포넌트를 만들 수 있게 된다. 정리하면, ButtonHTMLAttributes 같은 것들을 ButtonProps를 정의할 수 있음을 알 수 있다. 그리고 처음에 typescript 로 표현한 예시는 size props를 정해진 것만 통과하게 했다. 또한 ButtonHTMLAttributes 정의 없이 모든 props를 만들어도 된다. 각각의 경우에 따라 장단점이 있다. 구현하는 프로젝트에 따라 정책을 정해주면 된다.
위의 인터페이스들의 이름은 복잡해 보인다. 하지만 HTML요소명Element 형태의 이름 규칙을 발견 할 수 있다.
3. HTMLButtonElement
HTMLElement의 직관적인 구조는 아래와 같다. 우리는 button에 대해 조금더 깊게 알아보자.
HTMLElement 에 HTMLButtonElement이 포함된다는 것은 위에서 알아보았다. HTMLElement에 포함되는 개념 중에 대표로 HTMLButtonElement를 알아보도록 한다.
button의 type으로는 submit, reset, button으로 세가지가 있다. 세 가지에 따라 다른 역할을 한다. 기본값은 submit 이다. 즉 아래의 두 버튼은 같은 버튼이다.
<button>123</button>
<button type="submit">123</button>
form 내부에서 쓰면, 자동으로 submit이 되고 새로고침이 된다. 그러나 form 내에서 안쓰면 상관이 없긴하다. 헷갈린다면, 그냥 type을 button으로 고정으로 적고 이벤트에서 로직을 작성해 주는게 좋다. 이러한 type은 HTMLButtonElement.type에 속성값이 들어있다.
프로젝트를 진행하는 도중에 공통된 버튼이 있어 컴포넌트로 버튼을 하나 만들었다. 위의 고려사항들을 모두 고려해서 컴포넌트를 만들 수 있을까? 없다. 왜냐하면, 간단해 보이는 버튼 하나도 아래와 같이 규약이 존재하기 때문이다.
웹 규약을 고려하지 않은 컴포넌트는 좋지않다. 좋은 컴포넌트를 만들고자하는 시도는 좋지만, 우리는 웹을 이루는 기본에 대한 생각을 하면서 새로운 컴포넌트를 만들어야한다.
Frontend를 최근 배운 사람들은 이 글을 읽지 않을 것이다. 아마 typescript를 접하면서 궁금한 부분이 있는 사람들을 타겟으로 기초적인 부분들을 풀어보았다. 넓은 범위를 간략하게 지나갔기 때문에 궁금한 점이 있다면 조금 더 구글링을 해보도록 하자.
'Web' 카테고리의 다른 글
브라우저 렌더링 (0) | 2023.07.25 |
---|---|
브라우저(web browser)의 기본 정리 (0) | 2023.07.05 |
MERN Stack 으로 가장 빠르고 쉽게 Web만들기 (0) | 2022.04.19 |
[Error] fatal: in unpopulated submodule (0) | 2022.03.16 |
[아키텍쳐] Layered pattern (0) | 2022.03.10 |
- Total
- Today
- Yesterday
- typescript
- vuejs
- useState
- BFS
- UserCreationForm
- Express
- react
- Queue
- Vue
- pandas
- NextJS
- nextjs autoFocus
- read_csv
- 클라우데라
- JavaScript
- Python
- 자연어처리
- login
- mongoDB
- useHistory 안됨
- Deque
- django
- error:0308010C:digital envelope routines::unsupported
- nodejs
- react autoFocus
- DFS
- next.config.js
- TensorFlow
- 자료구조
- logout
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |