LogoSEO Jing
  • All Posts
  • SEO Jing
  • okayJing
  • KD Team
  • CLAB Coreteam
  • Study

Contact Me

© 2026 SEOJing. All rights reserved.

CSSUX모바일DevLog

모바일 웹에서 가로 모드를 강제하는 5가지 방법 — iOS Safari에서도 동작하는 코드 뷰어 만들기

2026년 3월 22일·15분 읽기

문제: 모바일에서 코드가 읽기 어렵다

블로그에 코드 블록이 많다. PC에서는 문제없지만, 모바일 세로 화면에서는 가로 스크롤 없이 코드를 읽기가 어렵다. 320px - 390px 폭에 들어가는 코드 라인은 40자에서 50자 정도인데, 실제 코드는 80~120자인 경우가 많다.

해결하고 싶은 것은 단순하다. 버튼 하나로 코드 블록을 가로 방향으로 넓게 보여주기. 네이티브 앱이라면 화면 회전을 강제하면 끝이지만, 웹에서는 그게 쉽지 않다.


방법 1: Screen Orientation API

가장 먼저 떠오르는 방법이다. screen.orientation.lock('landscape')를 호출하면 화면이 가로로 고정된다.

js
// 가로 모드 강제
async function forceLandscape() {
  try {
    await screen.orientation.lock("landscape");
  } catch (e) {
    console.error("회전 잠금 실패:", e);
  }
}

Post Q&A

오케이징에게 물어보기

모바일 웹에서 가로 모드를 강제하는 5가지 방법 — iOS Safari에서도 동작하는 코드 뷰어 만들기 전체를 기준으로 질문과 피드백을 받아요.답을 본 뒤에는 이 내용을 댓글로 달아서 서징에게도 물어볼 수 있어요. 작성자가 직접 볼 수 있어요!

0/500

포스트 목록

/SEOJing
파일 15개, 폴더 1개
Cloudflare Workers에서 fs 모듈이 안 되는 이유와 해결법대표 이미지 자동화 실험 — 검색과 Codex 생성이 같은 경로로 붙었다본문 이미지를 나중에 넣는 게 아니라 — SEOJing 글쓰기 파이프라인에 시각 판단을 넣기모바일 웹에서 가로 모드를 강제하는 5가지 방법 — iOS Safari에서도 동작하는 코드 뷰어 만들기블로그 글을 PPT로 만들기 — DOM 클로닝 기반 프레젠테이션 모드100vh가 100%가 아닌 이유 — 모바일 뷰포트 단위 완전 정리Context로 퀴즈 컴포넌트를 만들다 막혀서 React.Children을 공부하게 된 이야기대표 이미지를 글마다 다시 붙이는 방식 — 사진 검색에서 리소그래프 배경까지글 위에 영상을 붙인다는 것 — SEOJing 요약 쇼츠 파이프라인localStorage 읽기에서 하이드레이션 에러가 터지는 이유 useSyncExternalStore로 해결useEffect cleanup과 의존성 배열 — 실전 버그 사례로 이해하는 생애주기vinext + GitHub Actions로 Cloudflare Workers 배포하기vinext 오픈소스 기여기: 한국어 slug가 RSC에서 이슈를 일으킨 이유RSC 환경에서 WebAssembly가 차단되는 이유 — Shiki에서 rehype-prism-plus로vinext는 왜 빠를까? — SSR, Vite, Edge, 그리고 Web Vitals까지

같은 섹션의 대표 이미지

39 posts · latest first
본문 이미지를 나중에 넣는 게 아니라 — SEOJing 글쓰기 파이프라인에 시각 판단을 넣기 글의 대표 이미지
SEO Jing26. 06. 22.

본문 이미지를 나중에 넣는 게 아니라 — SEOJing 글쓰기.

SEOJing에서 새 글을 쓸 때 대표 이미지와 본문 이미지를 빼먹지 않도록, 블로그 맵과 글쓰기 파이프라인 안에 시각 판단 단계를 넣은 과정을 정리했다.

26. 06. 22.SEOJing

문제는 제약이 너무 많다는 것이다.

  • iOS Safari: 완전히 미지원. screen.orientation.lock이 존재하지 않는다.
  • Android Chrome: 전체화면(Fullscreen API) 상태에서만 동작한다.
  • 데스크톱 브라우저: 대부분 무시한다.

즉, 전체화면 진입 → 가로 회전 잠금 → 코드 보기 → 잠금 해제 → 전체화면 해제라는 복잡한 흐름이 되고, 그마저도 iOS 사용자 절반을 버려야 한다. 현실적이지 않다.

js
// Android에서만 동작하는 전체화면 + 회전 조합
async function enterLandscapeFullscreen(element) {
  await element.requestFullscreen();
  await screen.orientation.lock("landscape");
}

// iOS에서는 requestFullscreen도 제한적
// Safari는 video 요소에서만 전체화면을 허용한다

방법 2: PWA manifest의 orientation

PWA의 manifest.json에서 화면 방향을 지정할 수 있다.
json
{
  "name": "My Blog",
  "display": "standalone",
  "orientation": "landscape"
}

이 방법은 앱으로 설치된 경우에만 작동한다. 일반 브라우저에서 접속하면 manifest의 orientation은 완전히 무시된다. 블로그를 PWA로 설치하는 사용자는 거의 없으므로, 이 방법도 탈락이다.

방법 3: 회전 유도 UI

직접 회전하지 않고, 사용자에게 "기기를 가로로 돌려주세요"라는 안내를 보여주는 방식이다. 게임이나 영상 앱에서 흔히 볼 수 있다.

css
/* 세로 모드일 때만 회전 안내 표시 */
.rotate-prompt {
  display: none;
}

@media (orientation: portrait) {
  .rotate-prompt {
    display: flex;
    /* 화면 중앙에 "기기를 돌려주세요" 아이콘 */
  }
}

구현은 간단하지만, 사용자 경험이 나쁘다. 코드 블록 하나를 보기 위해 물리적으로 폰을 돌려야 한다. 회전 잠금을 걸어둔 사용자는 설정까지 바꿔야 한다. "버튼 하나로 넓게 보기"라는 원래 목표와 거리가 멀다.

방법 4: CSS writing-mode

텍스트 방향 자체를 바꾸는 CSS 속성이다. writing-mode: vertical-rl을 쓰면 텍스트가 세로로 흐른다.

css
.rotated-text {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
}

코드 블록에는 완전히 부적합하다. 코드는 가로로 읽어야 하는데, writing-mode는 글자 하나하나의 배치 방향을 바꾸기 때문에 코드의 들여쓰기와 정렬이 전부 깨진다. 표(table)나 세로 텍스트 레이아웃에서는 유용하지만, 코드 뷰어에 쓸 수 있는 방법은 아니다.

방법 5: CSS transform rotate — 가짜 가로 모드

남은 방법은 하나다. 화면은 세로 그대로 두고, UI만 90도 돌리는 것. transform: rotate(90deg)로 시각적 회전을 만들고, viewport 단위(100vh, 100vw)로 너비와 높이를 교체한다.

css
.fullscreen-rotated {
  position: fixed;
  inset: 0;

  .inner {
    position: absolute;
    width: 100vh; /* 세로 길이 > 가로 폭으로 */
    height: 100vw; /* 가로 길이 > 세로 폭으로 */
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%) rotate(90deg);
  }
}

이 방법은 모든 브라우저에서 동작한다. iOS Safari, Android Chrome, 데스크톱 브라우저 어디서든 CSS transform은 지원된다. Screen Orientation API와 달리 브라우저 제약이 없다.

원리는 단순하다.
  • width: 100vh — 뷰포트의 세로 길이를 가로 폭으로 사용
  • height: 100vw — 뷰포트의 가로 길이를 세로 폭으로 사용
  • translate(-50%, -50%) — 요소를 정확히 화면 중앙에 배치
  • rotate(90deg) — 시계 방향 90도 회전

결과적으로 사용자가 보는 화면은 가로 모드와 동일하다. 폰을 돌리지 않아도 된다.


구현하면서 부딪힌 UX 문제들

1. 코드가 중앙에서 시작되는 문제

처음에는 flex items-center justify-center로 회전 컨테이너를 화면 중앙에 놓았다. 그런데 이렇게 하면 코드가 화면 한가운데서 시작된다. 가로로 돌려서 넓이를 확보한 의미가 없어진다.

두 접근의 차이를 이해하려면, flex의 정렬 속성과 translate가 작동하는 레이어가 다르다는 것을 알아야 한다.

items-center justify-center는 flex 컨테이너가 자식의 위치를 결정한다. 컨테이너 안의 모든 자식이 수직·수평 모두 중앙으로 밀린다. 그래서 코드 블록도 화면 중앙에 놓이고, 스크롤 시작점도 가운데가 된다. flex-1을 줘도 자식이 남는 공간을 채우긴 하지만, 남는 공간 자체가 양쪽으로 균등하게 분배되기 때문에 결과는 같다.

css
/* 자식의 콘텐츠까지 중앙 정렬됨 */
.container {
  display: flex;
  align-items: center;
  justify-content: center;
}

.container .code {
  flex: 1; /* 의미 없음 — 이미 중앙 정렬 */
}

반면 translate(-50%, -50%)는 요소 자체의 렌더링 위치만 이동 한다. top: 50%; left: 50%로 요소의 좌측 상단 꼭짓점을 화면 중앙에 놓고, translate(-50%, -50%)로 자기 크기의 절반만큼 되돌려서 시각적 중앙에 배치한다. 중요한 것은, 이 과정에서 요소 내부의 레이아웃에는 개입하지 않는다는 점이다. 내부에서 flex-col과 flex-1을 쓰면 코드 영역이 자연스럽게 상단부터 채워진다.

css
/* 컨테이너만 중앙 배치, 내부는 상단부터 시작 */
.container {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) rotate(90deg);
  display: flex;
  flex-direction: column;
}

.container .code {
  flex: 1; /* 남는 높이를 모두 차지, 코드가 위에서 시작 */
}
정리하면 이렇다.
방식중앙 정렬 대상내부 콘텐츠 시작점flex-1 효과
items-center justify-center자식 요소의 콘텐츠화면 중앙공간 분배가 중앙 기준
translate(-50%, -50%)요소의 렌더링 위치요소 내부 상단남는 공간을 아래로 채움

서브픽셀 렌더링 주의점

translate(-50%, -50%)에는 한 가지 함정이 있다. 요소의 너비나 높이가 홀수 픽셀일 때 -50%가 0.5px 단위로 계산되면서, 텍스트나 border가 흐릿하게 보이는 서브픽셀 렌더링 문제가 발생할 수 있다. 브라우저가 0.5px 오프셋에 있는 요소를 두 물리 픽셀에 걸쳐 안티앨리어싱하기 때문이다.

이 프로젝트의 캐러셀 컴포넌트에서도 translate를 사용하다가 비슷한 문제를 겪은 적이 있다. 해결 방법은 몇 가지가 있다.

  • will-change: transform 또는 transform: translateZ(0) — GPU 레이어를 강제하면 서브픽셀 계산이 더 정확해진다.
  • 짝수 크기 보장 — 컨테이너의 width와 height를 뷰포트 단위로 지정하면 보통 정수가 아니므로, round() 같은 CSS 함수로 보정하거나 받아들이는 선택을 한다.
  • flex 정렬로 대체 — 서브픽셀 문제가 심한 경우, 부모를 flex로 만들고 margin: auto로 자식을 중앙에 놓는 방식이 더 안전하다.

이번 코드 뷰어에서는 width: 100vh, height: 100vw로 뷰포트 크기를 그대로 사용하기 때문에 대부분의 기기에서 서브픽셀 이슈가 눈에 띄지 않는다. 하지만 만약 텍스트가 흐릿하게 보인다면, translateZ(0)을 추가하는 것만으로 해결될 가능성이 높다.

2. 닫기 버튼을 어디에 놓든 누군가는 불편하다

처음에는 일반적인 모달처럼 닫기 버튼(X)을 우상단에 배치했다. 회전된 상태에서 이 위치는 실제 폰의 우측 상단이 된다. 한 손으로 폰을 잡고 엄지로 닿기 어려운 곳이다.

그래서 바를 하단으로 옮겼다. 회전된 화면의 하단은 실제 폰의 좌측 끝이다. 하지만 이것도 오른손잡이에게만 편한 위치였다. 결국 닫기 버튼을 어디에 놓든, 사람마다 폰을 쥐는 손이 다르기 때문에 누군가는 불편하다.

해결책은 닫기 버튼 자체를 없애는 것이었다. 대신 화면 아무 곳이나 1.5초간 길게 누르면 종료되도록 했다. 왼손이든 오른손이든, 엄지가 화면의 어디에 있든 동작한다. 하단 바에는 "화면을 1.5초간 누르면 종료"라는 안내 문구만 넣었다.

tsx
const LONG_PRESS_MS = 1500;

function FullscreenView({ onClose, children }) {
  const timerRef = useRef(null);
  const [pressing, setPressing] = useState(false);

  const handlePressStart = () => {
    setPressing(true);
    timerRef.current = setTimeout(() => {
      onClose();
    }, LONG_PRESS_MS

롱프레스의 장점은 실수로 닫히지 않는다는 것이다. 코드를 스크롤하다가 손가락이 미끄러져도 탭이지 길게 누르기가 아니므로 전체보기가 유지된다. 의도적으로 "이제 나가야지"라고 생각하고 꾹 누를 때만 종료된다.


방법별 비교 정리

방법iOS SafariAndroid Chrome전체화면 필요사용자 행동 필요
Screen Orientation API미지원전체화면에서만필요불필요
PWA manifest앱 설치 시만앱 설치 시만-

CSS transform rotate만이 모든 브라우저에서 동작하면서 사용자에게 추가 행동을 요구하지 않는 유일한 방법이다. 네이티브 회전이 아니라 시각적 트릭이라는 한계가 있지만, 코드 블록 전체보기라는 용도에는 충분하다.

핵심 교훈

웹에서 "당연히 될 것 같은" 기능이 플랫폼 제약에 막히면, API 수준에서 해결하려고 하기보다 CSS로 시각적으로 우회하는 게 더 현실적인 경우가 있다. screen.orientation.lock()은 스펙이 명확하지만 브라우저 지원이 엉망이고, CSS transform은 해킹에 가깝지만 어디서든 동작한다.

그리고 기능 구현이 끝난 뒤에도 물리적 맥락을 생각해야 한다. 화면을 돌리면 닫기 버튼의 위치도 함께 돌아간다. "엄지가 닿는가?"라는 질문은 코드를 작성할 때는 떠오르지 않고, 실제로 폰을 들고 써봐야 보이는 문제다. 결국 닫기 버튼의 위치를 고민하다가 버튼 자체를 없애고 롱프레스로 바꿨는데, 때로는 UI 요소를 더 잘 배치하는 것보다 아예 없애는 게 더 나은 답이다.

SEO Jing26. 06. 22.

글 위에 영상을 붙인다는 것 — SEOJing 요약.

SEOJing 글을 소셜용 영상으로 따로 소비시키는 게 아니라, 포스트 상단 요약과 블로그 유입 장치로 연결하기 위해 summaryVideo frontmatter와 Supertonic3 기반 요약 쇼츠 파이프라인을 붙인 과정을 정리했다.

26. 06. 22.SEOJing
대표 이미지 자동화 실험 — 검색과 Codex 생성이 같은 경로로 붙었다 글의 리소그래프 스타일 대표 이미지 배경
SEO Jing26. 06. 21.

대표 이미지 자동화 실험 — 검색과 Codex 생성이 같은.

SEOJing 블로그에 대표 이미지를 자동으로 붙이는 실험을 실제로 돌려봤다. 검색 기반 cover 삽입과 Codex CLI 기반 정적 SVG 생성이 같은 frontmatter 경로로 연결됐다.

26. 06. 21.SEOJing
대표 이미지를 글마다 다시 붙이는 방식 글의 리소그래프 스타일 대표 이미지 배경
SEO Jing26. 06. 21.

대표 이미지를 글마다 다시 붙이는 방식 — 사진 검색에서.

SEOJing 포스트 목록을 파일 탐색기처럼만 두지 않고, 최신 글부터 실제 사진 기반 리소그래프 배경을 붙이는 실험을 정리합니다. 이미지는 배경만 만들고, 제목과 아이콘은 블로그 UI가 맡는 쪽으로 방향을 바꿨습니다.

26. 06. 21.SEOJing
SEO Jing26. 04. 01.

Day 12 - 테스트 커버리지 개선, 모바일 프레젠테이션 버그 2건.

SEO Jing 개발 열두째 날. code-block 테스트 14개 추가로 커버리지 대폭 개선, 모바일 프레젠테이션에서 FullscreenView 방향 전환 문제와 스크롤 멈춤 버그 수정.

26. 04. 01.SEOJing
SEO Jing26. 04. 01.

useEffect cleanup과 의존성 배열 — 실전 버그.

useEffect 의존성 배열에 불필요한 값이 포함되면 cleanup과 재실행이 뒤엉켜 DOM 상태가 꼬일 수 있다. 프레젠테이션 모드에서 발생한 모바일 스크롤 고착 버그를 통해 원인과 해결 패턴을 정리한다.

26. 04. 01.SEOJing
SEO Jing26. 03. 25.

Day 11 - 프레젠테이션 확대 기능,.

SEO Jing 개발 열한째 날. PC 프레젠테이션 확대/축소 컨트롤 추가, 모바일 orientation 판단 로직 개선, FullscreenView를 독립 컴포넌트로 분리 및 PC 대응.

26. 03. 25.SEOJing
SEO Jing26. 03. 25.

vinext 오픈소스 기여기: 한국어 slug가 RSC에서.

한국어 MDX 블로그를 만들다 vinext 프레임워크의 ByteString 버그를 발견하고, 이슈를 작성하고, PR을 올리기까지의 과정

26. 03. 25.SEOJing
SEO Jing26. 03. 25.

vinext는 왜 빠를까? — SSR, Vite, Edge,.

vinext가 빠른 이유를 이해하기 위해, SSR부터 Hydration, 빌드 도구, Edge Runtime, Web Vitals, RSC, CDN 캐싱, ISR, PPR까지 웹 렌더링 성능의 전체 그림을 정리한다

26. 03. 25.SEOJing
SEO Jing26. 03. 24.

100vh가 100%가 아닌 이유 — 모바일 뷰포트 단위 완전 정리.

모바일 Safari에서 100vh가 화면을 넘치는 이유, vh/svh/lvh/dvh의 차이, JavaScript에서 실제 뷰포트를 구하는 방법, 그리고 전체화면 UI를 만들 때 알아야 할 CSS zoom과 모바일 판정 패턴까지 정리한다.

26. 03. 24.SEOJing
SEO Jing26. 03. 23.

Day 10 - 프레젠테이션 모드 안정화.

SEO Jing 개발 열째 날. 프레젠테이션 모드의 모바일 UX 문제들을 전면 수정. 퀴즈·코드블록·이미지·포스트목록 처리 개선, 롱프레스 UX 및 하단 바 레이아웃 안정화. 모바일 뷰포트·리스트 분할 문제 수정, 채움 비율 보수적으로 조정, 포스트 탐색기 자연 정렬 적용.

26. 03. 23.SEOJing
SEO Jing26. 03. 22.

Day 9 - 프레젠테이션 모드, 코드블럭 개선, 테스팅.

SEO Jing 개발 아홉째 날. 프레젠테이션 기능 추가, 퀴즈 구조 변경, 모바일 반응형, 코드블럭 사용성, 테스팅 도입.

26. 03. 22.SEOJing
SEO Jing26. 03. 22.

모바일 웹에서 가로 모드를 강제하는 5가지 방법 — iOS.

모바일 웹에서 코드 블록을 가로로 넓게 보여주고 싶었다. screen.orientation.lock()은 iOS에서 안 되고, PWA manifest는 브라우저에서 무시된다. 결국 CSS transform으로 가짜 회전을 만들었고, 그 과정에서 엄지 접근성까지 고민하게 됐다.

26. 03. 22.SEOJing
SEO Jing26. 03. 22.

블로그 글을 PPT로 만들기 — DOM 클로닝 기반.

MDX 파일을 수정하지 않고, 렌더된 DOM을 h2 기준으로 자르고 화면 높이에 맞춰 자동 페이지네이션하는 프레젠테이션 모드를 만들었다. 리스트 높이 측정이 왜 틀리는지 디버깅한 과정과, ul/ol을 li 단위로 분할하는 해결책을 정리한다.

26. 03. 22.SEOJing
SEO Jing26. 03. 21.

Day 8 - 아티클 퀴즈와 스터디 자료.

SEO Jing 개발 여덟째 날. 아티클 퀴즈 디자인시스템 구현과 스터디 대면 자료 작성.

26. 03. 21.SEOJing
SEO Jing26. 03. 21.

Context로 퀴즈 컴포넌트를 만들다 막혀서.

MDX 블로그에 퀴즈 컴포넌트를 만들면서, Context 기반 Compound Component로 시작했다가 index 문제에 막혀 React.Children API를 채택하게 된 과정을 정리한다.

26. 03. 21.SEOJing
SEO Jing26. 03. 20.

Day 7 - Front Matter CMS와 관련 게시물.

SEO Jing 개발 일곱째 날. Front Matter CMS 설치와 관련 게시물 이동 탐색기 구현.

26. 03. 20.SEOJing
SEO Jing26. 03. 18.

Day 6 - 스터디 자료 작성과 데스크탑 비율 수정.

SEO Jing 개발 여섯째 날. 데스크탑 비율 수정과 씨랩 스터디 사전 진단 자료 작성.

26. 03. 18.SEOJing
SEO Jing26. 03. 17.

Day 5 - shiki 제거, MDX 모듈화, 그리고.

SEO Jing 개발 다섯째 날. shiki를 rehype-prism-plus로 교체하고, gray-matter 직접 구현, MDX 모듈화, 페이지 내 검색, 테이블 디자인시스템까지.

26. 03. 17.SEOJing
SEO Jing26. 03. 16.

Day 4 - 배포와 CI/CD.

SEO Jing 개발 넷째 날. lint, codecov, Cloudflare 배포, fs 런타임 이슈.

26. 03. 16.SEOJing
SEO Jing26. 03. 16.

Cloudflare Workers에서 fs 모듈이 안 되는 이유와.

배포 후 블로그 포스트가 404를 반환하던 문제부터, gray-matter eval 차단, next-mdx-remote eval 차단까지 — 세 겹으로 터진 이슈를 하나씩 해결한 기록

26. 03. 16.SEOJing
SEO Jing26. 03. 16.

localStorage 읽기에서 하이드레이션 에러가 터지는 이유.

localStorage를 읽는 컴포넌트에서 하이드레이션 불일치가 발생하는 원인과, useState+useEffect가 아닌 useSyncExternalStore가 정답인 이유를 정리한다.

26. 03. 16.SEOJing
SEO Jing26. 03. 16.

vinext + GitHub Actions로.

vinext 프로젝트를 GitHub Actions로 Cloudflare Workers에 자동 배포하는 방법과 실제 겪은 트러블슈팅 기록

26. 03. 16.SEOJing
SEO Jing26. 03. 16.

RSC 환경에서 WebAssembly가 차단되는 이유 —.

코드 하이라이팅에 Shiki를 쓰면 왜 RSC에서 WebAssembly.instantiate() 에러가 터지는지, 그리고 빌드 타임 하이라이팅으로 어떻게 해결했는지 정리한다.

26. 03. 16.SEOJing
SEO Jing26. 03. 15.

엄청난 피드백.

CLI 코드 리뷰에서 받은 피드백과 전체 코드 수정 계획을 정리했다.

26. 03. 15.SEOJing
SEO Jing26. 03. 15.

생각보다 어려웠던 댓글, 완독 로컬스토리지.

localStorage만으로 글 읽기 추적, 스크롤 진행률, 댓글 감지를 구현한 과정을 정리했다.

26. 03. 15.SEOJing
SEO Jing26. 03. 15.

MDX 관련 이슈 노트.

블로그 디테일 페이지에서 MDX를 렌더링하기 위해 검토한 라이브러리들과 최종 선택 과정.

26. 03. 15.SEOJing
SEO Jing26. 03. 15.

Day 3 - MDX 이슈, 반응형, 다크모드.

SEO Jing 개발 셋째 날. MDX 라이브러리 이슈, 반응형, 코드블럭, 댓글, 다크모드, 코드 리뷰.

26. 03. 15.SEOJing
SEO Jing26. 03. 14.

디자인 시스템을 구축할 때 주의할 점.

디자인 시스템 구현 시 파일 구조, 디자인 토큰, 유의 사항을 정리했다.

26. 03. 14.SEOJing
SEO Jing26. 03. 14.

폰트는 왜 메인 페이지에서만 적용이 안되고 있었을까?.

Tailwind v4 환경에서 폰트가 메인 페이지에서만 적용되지 않던 원인과 Hydration Mismatch 이슈를 정리했다.

26. 03. 14.SEOJing
SEO Jing26. 03. 14.

MDX DOM 트리 파싱하기.

MDX 파일의 경로 탐색 로직과 콘텐츠 트리 생성 과정을 정리했다.

26. 03. 14.SEOJing
SEO Jing26. 03. 14.

결국 Node.js 까지 와버렸다.

MDX 파일 구조를 JSON으로 변환하기 위해 Node.js의 fs 모듈을 배워봤다.

26. 03. 14.SEOJing
SEO Jing26. 03. 14.

Day 2 - 블로그 스켈레톤과 MDX 파싱.

SEO Jing 개발 둘째 날. 디자인 시스템 확장, 블로그 스켈레톤, 폰트 이슈 해결.

26. 03. 14.SEOJing
SEO Jing26. 03. 13.

전체적인 플로우.

SEO Jing 프로젝트의 기술 스택 선정과 전체적인 개발 플로우 정리.

26. 03. 13.SEOJing
SEO Jing26. 03. 13.

Storybook으로 디자인 시스템 테스팅하기.

Storybook의 사용법과 디자인 시스템 개발에서의 장점을 정리했다.

26. 03. 13.SEOJing
SEO Jing26. 03. 13.

MDX가 뭘까?.

MDX의 개념과 블로그에서 활용하는 이유를 정리했다.

26. 03. 13.SEOJing
SEO Jing26. 03. 13.

Day 1 - 디자인 컨셉과 디자인 시스템.

SEO Jing 개발 첫째 날. 디자인 컨셉 설정과 디자인 시스템 구축을 시작했다.

26. 03. 13.SEOJing
SEO Jing26. 03. 13.

기술 블로그를 직접 제작하게 된 이유.

SEO Jing을 개발하게 된 이유입니다.

26. 03. 13.SEOJing
SEO Jing26. 03. 13.

왜 자꾸 프로젝트가 중단되는지.

프로젝트가 중단되는 이유에 대한 자기 회고입니다.

26. 03. 13.SEOJing
)
;
};
const handlePressEnd = () => {
setPressing(false);
clearTimeout(timerRef.current);
};
return (
<div
onTouchStart={handlePressStart}
onTouchEnd={handlePressEnd}
onTouchCancel={handlePressEnd}
>
<pre>{children}</pre>
<div>
<span>{language}</span>
<span>
{pressing ? "놓지 마세요..." : "화면을 1.5초간 누르면 종료"}
</span>
</div>
</div>
);
}
앱 설치
회전 유도 UI지원지원불필요폰 물리 회전
CSS writing-mode지원지원불필요불필요
CSS transform rotate지원지원불필요불필요