|
| 1 | +--- |
| 2 | +title: "Isomorphic 이란 뭐고 어떨때 사용하는 네이밍일까 ? (feat. useIsomorphicEffect 는 뭘까)" |
| 3 | +createdAt: 2025-10-30 |
| 4 | +category: React |
| 5 | +description: 오픈소스를 보다보면 Isomorphic 이라는 단어를 마주치게 됩니다. Isomorphic 이란 무엇인지, 그리고 어떨때 사용하는 네이밍인지에 대해 알아봅니다. 또 리액트에서 자주 사용되는 useIsomorphicEffect 훅도 함께 살펴봅니다. |
| 6 | +comment: true |
| 7 | +head: |
| 8 | + - - meta |
| 9 | + - name: keywords |
| 10 | + content: Isomorphic, Isomorphic JavaScript, React, useIsomorphicEffect, 동형 사상, 동일한 인터페이스, 다른 구현, 소프트웨어 공학, 리액트 훅, 서버 사이드 렌더링, 클라이언트 사이드 렌더링, SSR, CSR |
| 11 | +--- |
| 12 | + |
| 13 | +리액트 오픈소스들을 보다보면 `Isomorphic` 이라는 단어를 마주치게 됩니다. <br/> |
| 14 | +예를들어, `useIsomorphicEffect` 같은 훅이 그렇습니다. 한번씩 본것 같지 않나요 ㅎㅎ |
| 15 | + |
| 16 | +그렇다면 Isomorphic 이란 무엇인지, 그리고 어떨때 사용하는 네이밍인지에 대해 알아봅시다 |
| 17 | + |
| 18 | +## 🤔 동일한 인터페이스, 다른 구현 : Isomorphic |
| 19 | + |
| 20 | +> 수학에서 동형 사상은 서로 구조가 같은 두 대상 사이에, 모든 구조를 보존하는 사상이다. <br/> |
| 21 | +> 두 대상 사이에 동형 사상이 존재하는 경우 서로 동형이라고 하며, 서로 동형인 두 대상은 구조가 같아 구조로서 구별할 수 없다. <br/> |
| 22 | +> (출처 : 위키백과 - [동형 사상](https://ko.wikipedia.org/wiki/%EB%8F%99%ED%98%95_%EC%82%AC%EC%83%81)) |
| 23 | +
|
| 24 | +Isomorphic 이라는 단어는 그리스어에서 유래되었으며, `iso`는 "동일한"을, `morph`는 "형태"를 의미합니다. <br/> |
| 25 | +따라서 Isomorphic은 `동일한 형태를 가진` 이라는 뜻을 가지고 있습니다. |
| 26 | + |
| 27 | +## 🛠️ 소프트웨어 공학에서의 Isomorphic ? |
| 28 | + |
| 29 | +소프트웨어 개발에서 Isomorphic이라는 용어는 동일한 인터페이스를 가지지만, 서로 다른 구현을 가진 시스템이나 컴포넌트를 설명할 때 사용됩니다. |
| 30 | + |
| 31 | +Isomorphic은 원래 서버와 클라이언트가 동일한 코드 구조(형태) 를 공유하지만, 환경에 따라 다르게 동작하는 코드를 설명할 때 자주 사용됩니다. |
| 32 | + |
| 33 | +예를 들어 Isomorphic JavaScript는 |
| 34 | +브라우저(클라이언트)와 Node.js(서버) 양쪽 모두에서 동일한 로직이 실행될 수 있도록 작성된 코드를 말합니다. |
| 35 | +다음과 같이 서로의 역할은 다르지만, `하나의 코드가 양쪽의 환경에서 동작하는 형태`이기 때문에 Isomorphic 이라고 부릅니다. |
| 36 | + |
| 37 | +> 클라이언트: DOM 조작, 이벤트 처리, 렌더링 <br/> |
| 38 | +> 서버: 데이터 Fetch, 초기 HTML 생성(SSR) |
| 39 | +
|
| 40 | +<br/> |
| 41 | + |
| 42 | +이건 JavaScript 에서도 있는데요, <br/> |
| 43 | +`globalThis` 는 브라우저와 Node.js 환경에서 동일한 전역 객체에 접근할 수 있도록 해주는 Isomorphic API입니다. |
| 44 | + |
| 45 | +```javascript |
| 46 | +// 브라우저에서 |
| 47 | +globalThis === window; // true |
| 48 | + |
| 49 | +// Node.js에서 |
| 50 | +globalThis === global; // true |
| 51 | +``` |
| 52 | + |
| 53 | +## 🪄 Isomorphic 네이밍은 언제 사용될까? |
| 54 | + |
| 55 | +그럼 Isomorphic 이라는 네이밍이 언제 사용될지 감이 잡힐것입니다 ㅎㅎ<br/> |
| 56 | + |
| 57 | +| 상황 | 예시 | 설명 | |
| 58 | +| ------------------------------------------------------- | --------------------- | -------------------------------------------- | |
| 59 | +| 환경에 따라 다른 구현을 가지지만 동일한 API를 제공할 때 | `useIsomorphicEffect` | SSR/CSR 모두에서 안전하게 동작 | |
| 60 | +| 서버와 클라이언트에서 동일한 로직을 재사용할 때 | `isomorphic-fetch` | fetch를 Node.js와 브라우저에서 공통으로 사용 | |
| 61 | +| 플랫폼별로 다르게 구현되지만 같은 기능을 보장할 때 | `isomorphic-storage` | localStorage vs in-memory storage | |
| 62 | + |
| 63 | +## ❌ Isomorphic 네이밍을 쓰면 안 되는 경우 |
| 64 | + |
| 65 | +Isomorphic은 "동일한 인터페이스를 유지한 채, 환경에 따라 자동으로 구현이 달라지는 구조"를 의미합니다. <br/> |
| 66 | +즉, `사용자가 환경을 인식하지 않고도 동일한 코드를 쓸 수 있어야` Isomorphic이라고 부를 수 있습니다. |
| 67 | + |
| 68 | +예를들어 다음과 같은 네이밍은 적절하지 않습니다 |
| 69 | + |
| 70 | +```typescript |
| 71 | +import { useQuery, useSuspenseQuery } from "react-query"; |
| 72 | + |
| 73 | +export const useIsomorphicQuery = (suspense: boolean) => { |
| 74 | + return suspense ? useSuspenseQuery() : useQuery(); |
| 75 | +}; |
| 76 | +``` |
| 77 | + |
| 78 | +왜냐하면 `suspense` 라는 플래그를 통해 사용자가 직접 환경을 인식하고 선택해야 하기 때문입니다. <br/> |
| 79 | +따라서 이 경우는 Isomorphic 이라는 네이밍을 쓰지 않는 것이 좋습니다. |
| 80 | + |
| 81 | +### ✅ 그럼 어떤 네이밍을 써야할까 ? |
| 82 | + |
| 83 | +이 경우는 `usePolymorphicQuery` 또는 `useAdaptiveQuery` 와 같은 네이밍이 더 적절합니다. <br/> |
| 84 | + |
| 85 | +| 패턴 | 설명 | 예시 네이밍 | |
| 86 | +| ------------------- | ------------------------------- | --------------------------------------------------------- | |
| 87 | +| Adaptive Pattern | 환경이나 조건에 자동으로 적응 | `useAdaptiveQuery()`, `useAutoQuery()`, `useSmartQuery()` | |
| 88 | +| Polymorphic Pattern | 같은 인터페이스지만 구현이 다름 | `usePolymorphicQuery()` | |
| 89 | + |
| 90 | +:::details 🤓 엥? Adaptive 도 "자동으로 적용" 이라고? Isomorphic 이랑 Adaptive 두개 비슷한거같은데... |
| 91 | + |
| 92 | +| 구분 | Isomorphic | Adaptive | |
| 93 | +| ----------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------- | |
| 94 | +| 핵심 개념 | 환경(플랫폼) 에 따라 자동으로 다른 구현이 실행되지만, 인터페이스는 동일 | 상태나 조건(성능, 네트워크, 사용자 설정 등)에 따라 전략을 바꾸는 코드 | |
| 95 | +| 결정 주체 | 환경 (ex. 서버 vs 클라이언트, Node vs Browser) | 로직 (ex. 속도 모드, 네트워크 품질, 사용자 옵션) | |
| 96 | +| 사용자 관점 | 환경을 전혀 의식하지 않아야 함 | 사용자가 옵션을 줄 수도 있음 | |
| 97 | +| 예시 | `useIsomorphicLayoutEffect` : SSR이면 `useEffect`, CSR이면 `useLayoutEffect` | `useAdaptiveQuery({ mode: 'suspense' })` : mode 에 따라 선택 | |
| 98 | +| 목표 | 코드가 어느 환경에서든 똑같이 작동하게 | 현재 상황에 가장 적절하게 작동하게 | |
| 99 | + |
| 100 | +::: |
| 101 | + |
| 102 | +## ⚛️ React 에서의 useIsomorphicEffect ? |
| 103 | + |
| 104 | +위의 예제에서 살펴본 것처럼, <br/> |
| 105 | +React 생태계에서는 "Isomorphic" 이라는 단어가 환경별로 다른 구현을 동일한 API로 추상화할 때 자주 사용됩니다. |
| 106 | + |
| 107 | +예를 들어, 브라우저에서는 `useLayoutEffect`가 DOM을 바로 조작하기 위해 사용되지만, <br/> |
| 108 | +`SSR(Server-Side Rendering)` 환경에서는 DOM이 없기 때문에 호출 시 경고가 발생합니다. |
| 109 | + |
| 110 | +이를 해결하기 위해 만들어진 것이 바로 `useIsomorphicEffect` 입니다. |
| 111 | + |
| 112 | +```typescript |
| 113 | +import { useEffect, useLayoutEffect } from "react"; |
| 114 | + |
| 115 | +export const useIsomorphicEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect; |
| 116 | +``` |
| 117 | + |
| 118 | +즉, 클라이언트 환경에서는 useLayoutEffect를, 서버 환경에서는 useEffect를 사용하지만 <br/> |
| 119 | +개발자는 단일 훅(useIsomorphicEffect)으로 통일된 인터페이스를 쓸 수 있습니다. |
| 120 | + |
| 121 | +## 🧐 왜 굳이 이런 훅이 필요할까? |
| 122 | + |
| 123 | +그렇다면 왜 굳이 이런 훅이 필요한지 알아보기전에 `useLayoutEffect` 와 `useEffect` 의 차이를 간단히 짚고 넘어가봅시다. |
| 124 | + |
| 125 | +- `useEffect` : 렌더링 후에 비동기적으로 실행됩니다. (화면이 그려진 후에 실행) <br/> |
| 126 | + - 주로 데이터 Fetch, 구독 설정 등 부수 효과를 처리할 때 사용됩니다. |
| 127 | +- `useLayoutEffect` : 렌더링 직후, 브라우저가 화면에 그리기 전에 동기적으로 실행됩니다. (CRP 의 paint 이전에 실행되어야 하는 작업) <br/> |
| 128 | + - 주로 DOM 측정, 레이아웃 조정 등 화면에 영향을 미치는 작업에 사용됩니다. |
| 129 | + |
| 130 | +그리고 두 훅은 모두 서버사이드에서는 실행되지 않습니다. <br/> |
| 131 | + |
| 132 | +이제 본론으로 돌아와서, <br/> |
| 133 | +서버 렌더링(SSR)은 문자열 HTML을 만드는 과정일 뿐, 브라우저의 렌더 트리/레이아웃 단계가 존재하지 않습니다. <br/> |
| 134 | +그래서 "페인트 전에 동기 실행, DOM 조작" 이라는 전제 자체가 성립 안 됨으로, 실행할 타이밍이 없고 다음과 같은 경고가 발생합니다. |
| 135 | + |
| 136 | +> Warning: useLayoutEffect does nothing on the server, because its effect runs after the DOM is updated... <br/> |
| 137 | +> ⚠️ 님아... useLayoutEffect는 페인트 전에 DOM 읽거나 조작하려는 훅인데, 서버는 DOM이 없잖아요.. |
| 138 | +
|
| 139 | +## 🧠 정리! |
| 140 | + |
| 141 | +> Isomorphic은 환경이 다르더라도 동일한 인터페이스를 유지하는 코드 구조를 의미한다. |
| 142 | +
|
| 143 | +React에서는 주로 SSR 호환성을 보장하기 위해, 클라이언트 전용 훅이나 API를 서버 환경에서도 안전하게 사용할 수 있도록 Isomorphic 추상화 계층을 두는 패턴에서 등장합니다. |
0 commit comments