• 로그인
  • 장바구니에 상품이 없습니다.

리액트 직접 만들기




프론트엔드 개발하려면 리액트, 뷰, 스벨트 같은 프레임워크를 필수로 다뤄야하는 시대인데

이런걸 굳이 왜 쓰는거냐면

"어떤 데이터가 변하면 그 데이터가 박혀있는 HTML도 자동으로 다시 그려주기" 기능이 들어있어서 씁니다. 

다른 기능도 있긴 한데 그게 제일 중요한 이유입니다.

하지만 문법이나 성능이 맘에 안드는 상남자들은 직접 하나 만들어서 쓰고 그렇습니다.

기본 원리만 알면 7살도 만들 수 있습니다.


어떤 식으로 만드냐면

- 옛날 리액트처럼 가상 html을 만들어서 변화를 감지한 뒤에 실제 html에 반영해도 되고

- 요즘 프레임워크처럼 특정 html만 딱 찝어서 스마트하게 변경해도 되고

- 스벨트처럼 컴파일러를 만들어서 html 자동 변경하는 코드를 생성해줘도 됩니다.

우리도 간단하게 코드 30줄 정도로 리액트 비슷한걸 하나 만들어보도록 합시다. 

프레임워크 동작원리 이해에도 도움될 것 같고

그리고 남들이 안하는거 만들어보면 코딩 잘하는 척 할 수 있습니다.



데이터 꽂는 기능

안녕하세요 제 나이는 <p data-bind="age"></p>

<script>
let data = {
age : 18,
name : '김득팔'
}
</script>


일단 html 파일을 하나 준비하고

이름과 나이 데이터를 변수에 저장해뒀는데 이 데이터를 html 안에 쉽게 박아넣을 수 있는 기능부터 만들어봅시다.

리액트같은 프레임워크를 쓰면 {data.age} 이런 식으로 데이터를 html에 넣을 수 있는데

이거 구현하려면 html을 파싱해야하고 약간 복잡해질거같으니까 더 쉽게 만들어봅시다.

data-bind="age" 이런 속성을 html 요소에 집어넣으면

age라는 이름의 데이터가 html에 자동으로 박히는 식으로 동작하게 만들어봅시다.

약간 Vue같은 느낌으로요




<script>
let html = document.querySelectorAll('[data-bind]')
html.forEach((a)=>{
let attribute = a.getAttribute('data-bind')
a.innerText = data[attribute]
})
</script>

밑에 이런 코드를 추가합시다.

- data-bind를 가진 html 요소를 찾아서

- 반복문 돌려서 하나하나 출력해보고

- "age"가 박혀있으면 data.age를 html에 집어넣으라고 했습니다.

그럼 기능 구현 끝




데이터 변경 감지하는 기능

하지만 age 데이터가 18에서 19로 변경이 되면

html도 자동으로 변하게 하는 기능을 만들어야하는데

하지만 데이터 변동을 어떻게 감지할지가 가장 큰 문제 아닐까요. 

고대의 프레임워크들은 setInterval() 이런걸 써서 주기적으로 값이 바뀌었나 검사해보고 그러기도 했지만

요즘 mz 세대는 Proxy라는 문법을 사용합니다.





Proxy는 비유하자면 쁘락치랑 비슷한 역할입니다.

아무 자료나 쁘락치로 감싸놓을 수 있는데

그럼 앞으로 자료를 읽고쓸 때마다 쁘락치가 그걸 가로채서 자기 코드를 몰래 실행해줍니다. 

그럼 이제 쁘락치 안에 "HTML을 다시 그려주세요" 코드를 짜놓으면 리액트스러운 기능구현 끝 아니겠습니까.


<script>
let proxy = new Proxy(data, {
get(obj, prop){
return obj[prop]
},
set(obj, prop, val){
obj[prop] = val
}
})
</script>

Proxy를 쓰려면 new Proxy(감쌀object자료, 설정) 입력하면 됩니다.

get안에는 자료를 읽을 때 대신 실행할 코드,

set안에는 자료를 변경할 때 몰래 실행할 코드 적으면 됩니다.


get, set 안에 파라미터도 꺼내쓸 수 있는데

obj는 지금 읽고 쓸 자료, prop은 그 자료의 어떤 속성을 수정중인지, val은 수정할 값을 알려줍니다.

그리고 자료 안의 어떤 속성을 읽고있는지도 알려줍니다.

그래서 위처럼 사용하면 원래 자료를 그대로 읽고 쓰는 착한 쁘락치를 만들 수 있습니다.


참고1. 원래 자료를 읽고 쓸 때는 Reflect라는걸 쓰는게 더 정확합니다.

참고2. 쁘락치를 부착했으면 let data가 아니라 let proxy 변수를 읽고 써야 쁘락치가 발동됩니다.



그럼 이제 set( ){ } 안에 "html 다시 그려주세요" 이런 코드를 집어넣으면 기능 완성 아닐까요?

왜냐면 쁘락치를 부착해놨으면 age 변수를 수정해버릴 때 set이 자동으로 발동되니까요.





<script>
let data = useData({
    age : 18,
    name : '김득팔'
  })

function useData(data){
let proxy = new Proxy(data, {
get(obj, prop){
return obj[prop]
},
set(obj, prop, val){
obj[prop] = val
}
})
}
</script>

그 전에 함수에 저장해놓고 재사용하도록 합시다.

그럼 함수에 넣었다 빼면 쁘락치가 장착되어 나오기 때문에 편리하지 않을까요?

리액트의 useState와 약간 비슷한 느낌의 기능입니다.





<script>
let data = useData({
    age : 18,
    name : '김득팔'
  })

function useData(data){
let proxy = new Proxy(data, {
get(obj, prop){
return obj[prop]
},
set(obj, prop, val){
obj[prop] = val
재렌더링()
}
})
}

function 재렌더링(){
let html = document.querySelectorAll('[data-bind]')
html.forEach((a)=>{
let attribute = a.getAttribute('data-bind')
a.innerText = data[attribute]
})
}
재렌더링()

</script>

set( ){ } 안에 "html 다시 그려주세요" 코드를 넣어봤습니다.

아까 처음에 썼던 "html에 데이터 박아넣기" 코드인데 그거 다시 써도 되긴 하잖아요.

아무튼 이제 data.age를 바꿔보면 자동으로 html도 재렌더링이 됩니다.

진짜 되나 실험해봅시다.

근데 이러면 약간 비효율적인게

지금 age를 변경하면 모든 html을 스캔해서 재렌더링하라고 하고 있습니다.

그건 비효율적이니까 실제로 바뀐 html만 변경하라고 해도 되지 않을까요?




실제로 바뀐 html만 변경해주세요

리액트는 이걸 구현하려고 가상 html을 만들어서 실제 html과 비교하고 바꾸고 심지어 컴포넌트를 처음부터 다시 그리고 그러는데

그건 과거의 유물이라 mz 스타일로 구현하는 방법도 있습니다.

아마 Vue 아니면 Solid에서 쓰는 방법일텐데 그냥 "html에 데이터 박아넣기" 이 코드를 저장해두고 재활용하는 겁니다.

a.innerText = data[attribute] 

이런 코드를 실행한 적이 있는데

아마 "html에 data.age 박아넣으셈" 이런 식으로 실행이 되었을겁니다.

이걸 실행했었으면 이걸 어디 따로 보관했다가 재활용하면 되는거 아니겠습니까.


{ age : "age에 있던 값을 html에 박아넣으세요" }

이런 식으로 object 자료를 하나 만들어서

1. 처음 data.age를 html에 박아넣을 때 age를 html에 박아넣는 코드 덩어리를 보관해둡니다.

(object 안에 함수 보관도 가능하니까 함수로 만들어 보관해두면 될 것 같군요)

2. age가 변경될 때마다 age와 관련된 코드를 object 자료에서 찾아서 실행해버립니다.

이러면 되겠습니다.

안녕하세요 제 나이는 <span data-bind="age"></span>

<script>
let data = useData({
    age : 18,
    name : '김득팔'
  })
const DB = {}

function useData(data){
let proxy = new Proxy(data, {
get(obj, prop){
return obj[prop]
},
set(obj, prop, val){
obj[prop] = val
if (DB[prop]){
          DB[prop]()
        }
return true
}
})
}

let html = document.querySelectorAll('[data-bind]')
html.forEach((a)=>{
let attribute = a.getAttribute('data-bind')
const update = () => {
    a.innerText = data[attribute]
    }
    update()
  DB[attribute] = update
})



</script>

그래서 1. const DB = {} 라고 코드 보관소를 하나 만들고

2. a.innerText = data[attribute] 이런 코드를 실행할 때 DB에 따로 추가해둡니다.

예를 들면 age 데이터를 html에 박을 때 { age : a.innerText = data['age'] } 대충 이런 코드가 추가되겠군요.

3. 그 다음에 set 안에서 데이터가 변경될 때 DB에서 관련 코드를 찾아서 실행해버립니다.

예를 들면 age 데이터가 변경되면 DB에서 age라는 이름으로 저장된 코드를 찾아서 실행해주겠군요.

아무튼 이렇게 해두면 age라는 데이터를 수정할 때마다

DB에 있던 그 age부분 html을 변경하는 코드덩어리도 실행이되니까 자동 재렌더링이 알아서 되겠네요.





응용

더 응용을 해보자면

- 코드를 DB에 자동으로 추가하려면?

- DB에 있던 코드 덩어리를 실행할 때 여러개를 모았다가 한 번에 업데이트하는 식으로 batch 처리를 하려면?

queueMicrotask라는걸 써도 되겠습니다.

- useEffect기능과 컴포넌트기능도 만들려면?

아무튼 이런걸 더하다보면 리액트 비슷한 프레임워크를 만들어서 사용할 수 있습니다.

실제로 코드 100줄 내외로 리액트 비슷한걸 구현해놓은 프로젝트도 있습니다.

여러분도 남자라면 리액트를 만들어쓰도록 합시다.







2025년 12월 21일

About

현재 월 700명 신규수강중입니다.

  (09:00~20:00) 빠른 상담은 카톡 플러스친구 코딩애플 (링크)
  admin@codingapple.com
  이용약관, 개인정보처리방침
ⓒ Codingapple, 강의 예제, 영상 복제 금지
top

© Codingapple, All rights reserved. 슈퍼로켓 에듀케이션 / 서울특별시 강동구 고덕로 19길 30 / 사업자등록번호 : 212-26-14752 온라인 교육학원업 / 통신판매업신고번호 : 제 2017-서울강동-0002 호 / 개인정보관리자 : 박종흠