본문 바로가기

전체 글

(54)
자바스크립트로 이미지 클립보드에 복사하기 navigator.clipboard.write API를 사용해서 이미지를 복사하는 방법과, 과정에서 만났던 오류와 해결 방법 정리1. 기본 이미지 복사하기navigator.clipboard.write 메서드를 사용하면 이미지 같은 바이너리 데이터(Blob) 를 클립보드에 넣을 수 있다.단, 주의할 점은 HTTPS 환경에서만 동작한다는 것.const copyImage = async () => { try { const response = await fetch(props.resultImg); let blob = await response.blob(); await navigator.clipboard.write([ new ClipboardItem({ [blob.type]: ..
custom-cursor-js 커서 커스터마이징 라이브러리 화면 구현 중 쓸만한 라이브러리를 하나 발견해서 정리해본다.커서 커스터마이징 라이브러리 소개최근 mojam.co 웹사이트에서 커서 모양이 독특하게 표시되는 것을 확인했다. 개발자 도구로 분석해보니, custom-cursor-js라는 라이브러리를 사용하고 있었다. 이 라이브러리를 활용하면 마우스 커서의 모양을 다양하게 바꿀 수 있기 때문에, 나중에 홈페이지의 UX나 디자인을 차별화하는 데 유용할 것 같다.custom-cursor-js 주요 특징커서를 다양한 이미지나 형태로 자유롭게 변경할 수 있다.기본 브라우저 커서를 숨기고 커스텀 cursor만 노출할 수 있다.상호작용에 따라 커서를 실시간으로 바꾸고, 텍스트, 미디어 등을 가리지 않게 설정할 수 있다.커서 효과를 특정 영역에서만 적용하거나 비활성화할 수..
Web Component Shadow DOM ::part()로 스타일 커스텀하기 웹 개발을 하다 보면 요즘은 Shadow DOM을 이용한 Web Component를 많이 마주치게 된다.예를 들어, Swiper의 swiper-container Web Component가 대표적이다.이런 Shadow DOM 기반 컴포넌트는 scoped되어 있기 때문에, 우리가 기존 CSS 방식으로 내부 요소를 스타일링할 수 없다.이럴 때 사용하는 것이 바로 part 속성과 ::part()다. 왜 part가 필요할까?일반적으로 우리가 컴포넌트를 만들 때, scoped CSS 덕분에 컴포넌트 안팎이 분리되듯, Web Component도 내부 구조가 외부 CSS에 영향을 받지 않게 만들어진다. #shadow-root 이렇게 Shadow DOM 안에 있으면, 우리는 .swiper-pag..
[Vue3] position: fixed로 생기는 iOS 렌더링 버그 Nuxt에서 커스텀 모달을 만들었는데, iOS Safari, 정확히는 앱의 웹뷰(WKWebView)화면에서 하단 콘텐츠가 안 보이는 문제가 발생했다. 문제 상황- 모달이 열릴 때 `position: fixed`로 스크롤을 막았고- 모달 내부에 스크롤 가능한 콘텐츠가 있었는데- 하단 배경이 하얗게 비어 있거나, 스크롤해야만 보이기 시작함 문제원인iOS Safari는 최적화를 위해, 초기 렌더링 시 뷰포트 밖에 있는 영역을 "Lazy paint" 처리한다.특히 position: fixed를 렌더링이 완전히 끝나기 전에 적용하면, 브라우저가 레이아웃을 조기 확정해버려하단 콘텐츠가 실제로 존재하더라도 렌더링되지 않는 문제가 발생한다. 사파리 개발자도구(Layers 패널)로 확인한 결과,하얗게 비어 있던 하단..
[Vue] Textarea 줄바꿈 제한하기 textarea에서 사용자 입력을 제한할 때, 단순한 maxlength만으로는 줄 수 제어가 되지 않아 직접 로직을 구현해 보았다.1. keydown.enter 이벤트로 줄바꿈 방지Enter 키 입력을 감지하여 줄 수를 초과하면 줄바꿈을 막는 방식을 시도했다.@keydown.enter="checkEnter($event, 5)" checkEnter 함수에서는 현재 입력값의 줄 수를 계산하여 5줄 이상일 경우 event.preventDefault()로 줄바꿈을 막고, 에러 메시지를 노출했다.const checkEnter = (event: KeyboardEvent, maxLine: number) => { const target = event.target as HTMLTextAreaElement; const..
[Nuxt] useAsyncData 중복 API 호출 막기 Nuxt3 프로젝트에서 페이지를 새로고침을 하거나 같은 데이터를 패칭할 때 API가 두 번 이상 호출되는 문제가 생겼다. 중복 호출이 발생하는 이유Nuxt3는 SSR과 클라이언트 하이드레이션을 동시에 지원한다. 이 과정에서 동일한 데이터 패칭 로직이 서버와 클라이언트에서 각각 한번씩 실행될 수 있다. dedupe 옵션useAsyncData의 dedupe 옵션은 동일한 key로 중복 호출되는 API 요청을 제어하는 기능이다.dedupe 옵션을 사용하여 동일 key의 요청이 동시에 여러 번 발생하지 않도록 보장할 수 있다.cancel (기본값): 새 요청이 들어오면 기존 요청을 취소하고 새로 요청을 처리.defer: 기존 요청이 진행 중이면 새로운 요청을 만들지 않고, 기존 요청이 끝날 때까지 기다림. 사용..
텍스트 에디터 사용 검토시 참고할만한 글 https://liveblocks.io/blog/which-rich-text-editor-framework-should-you-choose-in-2025 Which rich text editor framework should you choose in 2025? | Liveblocks BlogLooking to integrate a WYSIWYG editor into your JavaScript app? This comparison dives into the best frameworks available, including Tiptap, Lexical, BlockNote, and Slate.liveblocks.io
Swiper 사용 시 슬라이드가 부모 너비를 넘는 문제 Swiper 너비가 부모의 너비를 넘어 화면이 늘어나는 현상을 발견했다.문제 상황 4개의 이미지를 16px 간격으로 보여주는 레이아웃을 구성하려고 했다.하지만 ImageCard 컴포넌트 내부에서 flex: 1 0 0 스타일이 적용되어 있었고,이로 인해 슬라이드 하나의 너비가 Swiper에서 계산한 너비를 초과하는 문제가 발생했다. ImageCard는 여러 화면에서 재사용되는 컴포넌트이기 때문에,컴포넌트를 직접 수정하기보다는 Swiper 쪽에서 슬라이드의 너비를 조정하는 방식으로 해결하기로 했다. 해결 방법 .swiper-slide { max-width: calc((100% - 48px) / 4); // (100% - 간격) / 화면에 보일 수) b..