1. 브라우저 이벤트 루프(Event Loop)이벤트 루프가 처리하는 작업은 Task와 Microtask로 나뉘는데 이 Task가 일반적으로 Macrotask라 불리는 작업이다. WHATWG 명세에는 Macrotask라는 용어가 존재하지 않음에 유의하자.Event Loop Processing Model #1. Task Queue에서 작업 `oldestTask`을 하나 꺼낸다.2. `oldestTask`을 Event Loop의 currentlyRunningTask로 설정하고 처리한다. (Microtask는 여기에서 처리되는 task가 아니다!)3. currentlyRunningTask를 `null`로 되돌린다.4. Microtask 체크포인트 실행한다. i. Event Loop의 microta..
1. Renderer와 Reconciler #처음 React는 오직 DOM을 위해서만 개발되었지만 React Native와 같은 네이티브 플랫폼을 지원하게 되면서 Renderer와 Reconciler라는 개념이 생겨났다. 상태가 업데이트되어 UI가 업데이트되어야 한다고 했을 때, DOM과 Native에서 UI를 변경하는 방법은 다르다. 하지만 UI가 아닌 React 트리 구조가 어떻게 업데이트되어야 하는지는 두 플랫폼에서 동일한데 이것은 Reconciler의 역할이다. 정리하자면 Renderer는 React 컴포넌트 트리를 어떻게 각각의 플랫폼에 맞는 호출로 변환해 처리할 것인지를 결정한다면, Reconciler는 플랫폼에 상관없는 React의 컴포넌트, 상태, 생애주기 메서드 등이 일관되게 동작할 수 ..
const obj = { all: ['all'], list: () => [...obj.all, 'list'], detail: (id: string) => [...obj.all, 'detail', id],}이런 JavaScript 객체를 생각해보자. 이 객체의 list 속성의 함수는 내부에서 객체 `obj`를 참조하고 있는데 `obj`가 초기화되지 않은 상태에서 정의되는데 문제가 없을까? 놀랍게도(?) 객체 `obj`가 선언 및 초기화되는 것도, `obj.list()`가 실행되는 과정에서 아무런 에러가 발생하지 않는다. 함수 본문에서 참조하고 있기 때문에 문제가 없는 것 같기도 한데 JavaScript 함수에서 변수가 어떻게 참조되길래 이 코드가 정상적으로 선언되고 실행될까? 1. 실행 컨텍스트..
1. app 폴더 구조 #A. 기본 폴더 구조Next에서는 /app 폴더 내부에 각각의 경로 세그먼트(route segment)에 해당하는 폴더를 만듦으로써 라우팅이 가능하다. 예를 들어, /app/dashboard/page.js 컴포넌트는 www.mypage.com/dashboard 페이지로 연결된다. 이러한 방식으로 중첩 라우팅이 가능한데, /app/dashboard/setting/page.js 컴포넌트는 www.mypage.com/dashboard/setting 페이지에 해당한다. B. 파일 컨벤션 #① page.js: 해당 경로에 보여줄 UI 컴포넌트② route.js: ③ layout.js: 현재 경로 세그먼트와 자식 세그먼트의 레이아웃을 위한 UI 컴포넌트④ loading.js: 로딩 상태를..
1. Vite 작동 방식(개발 환경)A. 스크립트 파일 로드Vite의 React + TypeScript 탬플릿의 `index.html` 파일을 보면 script 태그에 tsx 파일이 그대로 들어가 있는 걸 볼 수 있다. 브라우저는 JavaScript 파일만 읽을 수 있는데 어떻게 문제가 없이 작동할 수 있는 걸까? `npm run dev`로 개발 서버(ex. http://localhost:3000/)를 시작하면 브라우저에서는 해당 URL의 html 파일을 Vite 개발 서버에 요청한다. 브라우저가 다운받은 html 파일은 아래와 같은데 위 템플릿 html에 스크립트가 추가되어 있다. react-refresh는 React 코드에 변경 사..
Figma의 코멘트 요소처럼 캔버스 노드 바로 위에 DOM 요소를 렌더링해야 하는 요구사항이 생기다 보니 react-konva-utils의 ``을 이용해 DOM 요소를 렌더링할 수 있도록 했다. 처음에는 간단한 UI 요소에만 ``을 사용했는데 점점 노드 설정 팝업 등 스타일링이 들어간 요소가 필요하게 되어 styled-components를 사용하게 됐는데 그 과정에서 styled-components의 theme을 제대로 가져오지 못하는 문제에 직면하게 됐다. 이 글은 react-konva-utils의 ``와 styled-components를 사용하면서 theme 설정을 제대로 가져오지 못하는 문제를 해결해나가는, 다소 허무한 여정을 정리한 것이다. 1. styled-components의 테마를 사용할..
제너레이터 함수에서 다음 yield 구문을 실행하기 위해서는 generator.next() 메서드를 실행해줘야 함. 하지만 각각의 제너레이터 함수에 대해 next()를 실행해주는 로직은 없는데 Redux-Saga 내부에서 어떠한 과정을 거치길래 이러한 동작이 가능할까?1. Saga 미들웨어는 어떤 과정을 거쳐 각각의 제너레이터 함수에 대해 next() 메서드를 실행시킬까?2. UI에서 액션이 dispatch될 때, 어떻게 suspend되어 있는 제너레이터 함수가 다시 재개될까? 아래와 같은 rootSaga를 Redux에 설정하는 경우를 예로 들어보자.import { createStore, applyMiddleware } from 'redux'import createSagaMiddleware from 'r..
1. 쿼리 캐시 무효화(Query Invalidation) #mutation으로 서버의 데이터를 수정한 경우, 해당 쿼리 키에 대응하는 데이터는 최신 상태가 아니므로 해당 데이터가 오래되었음을 Query Client에게 알려줄 필요가 있음. 이럴 때 사용할 수 있는 메서드가 `invalidateQueries()`.import { useQueryClient, useQuery } from "react-query";function Todos() { const queryClient = useQueryClient(); const addTodoMutation = useMutation({ mutationFn: (newTodo) => axios.post("/api/data", { text: newTodo })..
1. 확대/축소 기능 확대 기능을 추가하려는 요소에 wheel 이벤트 핸들러를 추가. 요소 자체의 크기를 변경해 확대/축소를 구현하지 않고, CSS transform - scale 속성을 이용함. const imageNode = document.querySelector('.image') let zoom = 1 let translateY = 0 let translateX = 0 imageNode.addEventListener('wheel', ({ deltaY }) => { // 원하는 감도로 상수 설정 const scale = zoom + (deltaY * -0.001) e.preventDefault() // 최대/최소 배율 설정 zoom = clampMinMax(scale, 1, 3) if (zoom {..
React에서 컴포넌트에 컴포넌트를 전달할 때 보통 children 속성을 이용하지만, 별도로 정의한 Props로 컴포넌트를 넘길 때에는 상황에 맞게 Props를 다른 방법으로 전달해야 함. 전달하려는 컴포넌트를 ① 함수형 컴포넌트로 정의, ② JSX 형태를 직접 변수로 정의하는 두 가지 방법이 있고, // 방법1 const ChildComponent = () => { return 자식 컴포넌트 } // 방법2 const childComponent = 자식 컴포넌트 컴포넌트를 Props로 전달하는 방법에는 ① 컴포넌트 자체를 전달, ② JSX 형태로 전달, ③ (props) => JSX.Element 형태로 화살표 함수로 래핑해서 전달하는 방법이 있음. } /> } /> 마지막으로 Props로 전달받은 ..
JavaScript/React 2025. 6. 13. 09:09
1. 브라우저 이벤트 루프(Event Loop)이벤트 루프가 처리하는 작업은 Task와 Microtask로 나뉘는데 이 Task가 일반적으로 Macrotask라 불리는 작업이다. WHATWG 명세에는 Macrotask라는 용어가 존재하지 않음에 유의하자.Event Loop Processing Model #1. Task Queue에서 작업 `oldestTask`을 하나 꺼낸다.2. `oldestTask`을 Event Loop의 currentlyRunningTask로 설정하고 처리한다. (Microtask는 여기에서 처리되는 task가 아니다!)3. currentlyRunningTask를 `null`로 되돌린다.4. Microtask 체크포인트 실행한다. i. Event Loop의 microta..
JavaScript/React 2025. 5. 17. 16:55
1. Renderer와 Reconciler #처음 React는 오직 DOM을 위해서만 개발되었지만 React Native와 같은 네이티브 플랫폼을 지원하게 되면서 Renderer와 Reconciler라는 개념이 생겨났다. 상태가 업데이트되어 UI가 업데이트되어야 한다고 했을 때, DOM과 Native에서 UI를 변경하는 방법은 다르다. 하지만 UI가 아닌 React 트리 구조가 어떻게 업데이트되어야 하는지는 두 플랫폼에서 동일한데 이것은 Reconciler의 역할이다. 정리하자면 Renderer는 React 컴포넌트 트리를 어떻게 각각의 플랫폼에 맞는 호출로 변환해 처리할 것인지를 결정한다면, Reconciler는 플랫폼에 상관없는 React의 컴포넌트, 상태, 생애주기 메서드 등이 일관되게 동작할 수 ..
JavaScript 2025. 2. 25. 10:33
const obj = { all: ['all'], list: () => [...obj.all, 'list'], detail: (id: string) => [...obj.all, 'detail', id],}이런 JavaScript 객체를 생각해보자. 이 객체의 list 속성의 함수는 내부에서 객체 `obj`를 참조하고 있는데 `obj`가 초기화되지 않은 상태에서 정의되는데 문제가 없을까? 놀랍게도(?) 객체 `obj`가 선언 및 초기화되는 것도, `obj.list()`가 실행되는 과정에서 아무런 에러가 발생하지 않는다. 함수 본문에서 참조하고 있기 때문에 문제가 없는 것 같기도 한데 JavaScript 함수에서 변수가 어떻게 참조되길래 이 코드가 정상적으로 선언되고 실행될까? 1. 실행 컨텍스트..
JavaScript/Next.js 2024. 12. 28. 18:16
1. app 폴더 구조 #A. 기본 폴더 구조Next에서는 /app 폴더 내부에 각각의 경로 세그먼트(route segment)에 해당하는 폴더를 만듦으로써 라우팅이 가능하다. 예를 들어, /app/dashboard/page.js 컴포넌트는 www.mypage.com/dashboard 페이지로 연결된다. 이러한 방식으로 중첩 라우팅이 가능한데, /app/dashboard/setting/page.js 컴포넌트는 www.mypage.com/dashboard/setting 페이지에 해당한다. B. 파일 컨벤션 #① page.js: 해당 경로에 보여줄 UI 컴포넌트② route.js: ③ layout.js: 현재 경로 세그먼트와 자식 세그먼트의 레이아웃을 위한 UI 컴포넌트④ loading.js: 로딩 상태를..
JavaScript/기타 라이브러리 2024. 11. 23. 16:15
1. Vite 작동 방식(개발 환경)A. 스크립트 파일 로드Vite의 React + TypeScript 탬플릿의 `index.html` 파일을 보면 script 태그에 tsx 파일이 그대로 들어가 있는 걸 볼 수 있다. 브라우저는 JavaScript 파일만 읽을 수 있는데 어떻게 문제가 없이 작동할 수 있는 걸까? `npm run dev`로 개발 서버(ex. http://localhost:3000/)를 시작하면 브라우저에서는 해당 URL의 html 파일을 Vite 개발 서버에 요청한다. 브라우저가 다운받은 html 파일은 아래와 같은데 위 템플릿 html에 스크립트가 추가되어 있다. react-refresh는 React 코드에 변경 사..
JavaScript/React 2024. 8. 29. 16:48
Figma의 코멘트 요소처럼 캔버스 노드 바로 위에 DOM 요소를 렌더링해야 하는 요구사항이 생기다 보니 react-konva-utils의 ``을 이용해 DOM 요소를 렌더링할 수 있도록 했다. 처음에는 간단한 UI 요소에만 ``을 사용했는데 점점 노드 설정 팝업 등 스타일링이 들어간 요소가 필요하게 되어 styled-components를 사용하게 됐는데 그 과정에서 styled-components의 theme을 제대로 가져오지 못하는 문제에 직면하게 됐다. 이 글은 react-konva-utils의 ``와 styled-components를 사용하면서 theme 설정을 제대로 가져오지 못하는 문제를 해결해나가는, 다소 허무한 여정을 정리한 것이다. 1. styled-components의 테마를 사용할..
JavaScript/기타 라이브러리 2024. 8. 3. 18:02
제너레이터 함수에서 다음 yield 구문을 실행하기 위해서는 generator.next() 메서드를 실행해줘야 함. 하지만 각각의 제너레이터 함수에 대해 next()를 실행해주는 로직은 없는데 Redux-Saga 내부에서 어떠한 과정을 거치길래 이러한 동작이 가능할까?1. Saga 미들웨어는 어떤 과정을 거쳐 각각의 제너레이터 함수에 대해 next() 메서드를 실행시킬까?2. UI에서 액션이 dispatch될 때, 어떻게 suspend되어 있는 제너레이터 함수가 다시 재개될까? 아래와 같은 rootSaga를 Redux에 설정하는 경우를 예로 들어보자.import { createStore, applyMiddleware } from 'redux'import createSagaMiddleware from 'r..
JavaScript/기타 라이브러리 2023. 11. 14. 14:21
1. 쿼리 캐시 무효화(Query Invalidation) #mutation으로 서버의 데이터를 수정한 경우, 해당 쿼리 키에 대응하는 데이터는 최신 상태가 아니므로 해당 데이터가 오래되었음을 Query Client에게 알려줄 필요가 있음. 이럴 때 사용할 수 있는 메서드가 `invalidateQueries()`.import { useQueryClient, useQuery } from "react-query";function Todos() { const queryClient = useQueryClient(); const addTodoMutation = useMutation({ mutationFn: (newTodo) => axios.post("/api/data", { text: newTodo })..
JavaScript 2023. 9. 2. 17:55
1. 확대/축소 기능 확대 기능을 추가하려는 요소에 wheel 이벤트 핸들러를 추가. 요소 자체의 크기를 변경해 확대/축소를 구현하지 않고, CSS transform - scale 속성을 이용함. const imageNode = document.querySelector('.image') let zoom = 1 let translateY = 0 let translateX = 0 imageNode.addEventListener('wheel', ({ deltaY }) => { // 원하는 감도로 상수 설정 const scale = zoom + (deltaY * -0.001) e.preventDefault() // 최대/최소 배율 설정 zoom = clampMinMax(scale, 1, 3) if (zoom {..
JavaScript/React 2023. 1. 18. 17:54
React에서 컴포넌트에 컴포넌트를 전달할 때 보통 children 속성을 이용하지만, 별도로 정의한 Props로 컴포넌트를 넘길 때에는 상황에 맞게 Props를 다른 방법으로 전달해야 함. 전달하려는 컴포넌트를 ① 함수형 컴포넌트로 정의, ② JSX 형태를 직접 변수로 정의하는 두 가지 방법이 있고, // 방법1 const ChildComponent = () => { return 자식 컴포넌트 } // 방법2 const childComponent = 자식 컴포넌트 컴포넌트를 Props로 전달하는 방법에는 ① 컴포넌트 자체를 전달, ② JSX 형태로 전달, ③ (props) => JSX.Element 형태로 화살표 함수로 래핑해서 전달하는 방법이 있음. } /> } /> 마지막으로 Props로 전달받은 ..