<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>summer.log</title>
        <link>https://velog.io/</link>
        <description>개발 lev.1</description>
        <lastBuildDate>Wed, 13 Nov 2024 03:29:40 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>summer.log</title>
            <url>https://velog.velcdn.com/images/yo_onzy/profile/d913e923-b0f9-4add-be0f-d3f19306f1a7/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. summer.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/yo_onzy" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[react-zoom-pan-pinch 라이브러리에 이미지 회전 + 확대 기능 추가하기]]></title>
            <link>https://velog.io/@yo_onzy/react-zoom-pan-pinch-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%97%90-%EC%9D%B4%EB%AF%B8%EC%A7%80-%ED%9A%8C%EC%A0%84-%ED%99%95%EB%8C%80-%EA%B8%B0%EB%8A%A5-%EC%B6%94%EA%B0%80%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@yo_onzy/react-zoom-pan-pinch-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%97%90-%EC%9D%B4%EB%AF%B8%EC%A7%80-%ED%9A%8C%EC%A0%84-%ED%99%95%EB%8C%80-%EA%B8%B0%EB%8A%A5-%EC%B6%94%EA%B0%80%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 13 Nov 2024 03:29:40 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>사용한 라이브러리 (v3.6.1 기준)
<a href="https://www.npmjs.com/package/react-zoom-pan-pinch">react-zoom-pan-pinch</a></p>
</blockquote>
<p>서론과 본론은 회고록이므로 코드만 확인하고 싶으시면 결론을 보시면 됩니다.  </p>
<hr>
<h3 id="서론">서론</h3>
<p>프로젝트 진행 중 이미지 미리보기 모달을 구현할 일이 있었는데, 편집 툴이 아닌 라이브러리 중에서 이미지 확대/축소와 회전 기능을 동시에 제공하는 걸 찾지 못했다.</p>
<p>물론 여러 스타일 프레임워크에서 지원하는 기능이긴 하지만, 이 기능만을 위해 스타일 프레임워크를 도입하는 건 비효율적이라 판단해서 react-zoom-pan-pinch 라이브러리를 조금 손봐서 회전 후에도 확대 기능을 문제없이 사용할 수 있게 해봤다.</p>
<hr>
<h3 id="본론">본론</h3>
<h4 id="문제점1-img-요소를-회전하면-transformcomponent와-축이-어긋남">문제점1. img 요소를 회전하면 TransformComponent와 축이 어긋남</h4>
<p>처음엔 간단하게 img 요소에 rotate 값을 주면 되겠다고 생각했다. 하지만 그렇게 하면 img 요소의 회전만 바뀌고 TransformComponent는 회전하지 않아서, TransformWrapper 영역에 img 요소가 일부 가려지게 된다.</p>
<p><img src="https://velog.velcdn.com/images/yo_onzy/post/5cc3e78a-798f-4fb3-b909-af67d2d789c0/image.png" alt=""></p>
<p>사진이 차지하는 영역이 img 요소이고, 파란 영역은 TransformComponent다.</p>
<p><img src="https://velog.velcdn.com/images/yo_onzy/post/62a4b5f8-ec37-4f96-8904-e913843a184a/image.gif" alt=""></p>
<p>이렇게 되면 img 요소는 왼쪽에 컨텐츠가 있는데, TransformComponent는 세로 방향으로 고정되어 있어서 TransformWrapper 영역 내에서 img 요소의 일부가 잘리거나 흰 여백이 생긴다.</p>
<p>그래서 두 번째로 생각한 건, <strong>TransformComponent도 같이 돌리면 되지 않을까?</strong> 하는 거였다.</p>
<hr>
<h4 id="문제점2-transformcomponent를-회전하면-마우스-이동축도-회전됨">문제점2. TransformComponent를 회전하면 마우스 이동축도 회전됨</h4>
<p>TransformComponent와 img의 방향을 맞추려면 TransformComponent 자체를 회전시키면 해결될 줄 알았다.</p>
<p>문제는 TransformComponent를 회전시키면 마우스 이동축도 함께 회전된다는 거다. 그래서 화면상에서 마우스를 왼쪽으로 드래그해도 회전된 이동축 때문에 img 요소는 위로 이동하게 된다.</p>
<p><img src="https://velog.velcdn.com/images/yo_onzy/post/13236a53-9c5a-4cc0-a098-b1e61b80d44f/image.gif" alt=""></p>
<p>이 문제를 해결하려고 회전 각도에 따른 마우스 이동축 보정값을 구해보려 했지만 실패했다.</p>
<hr>
<p>이 두 문제로 파악한 해결 조건은 다음과 같다.</p>
<blockquote>
<ol>
<li>img 요소와 TransformComponent의 방향을 동일하게 맞출 것.</li>
<li>TransformComponent는 회전시키지 않을 것.</li>
</ol>
</blockquote>
<p>실패 원인을 고찰하던 중 문득 <strong>img 요소만 회전시키되, TransformComponent의 너비와 높이를 회전된 img 요소와 같게 유지하면 되지 않을까?</strong> 하는 생각이 들었다.</p>
<hr>
<h4 id="문제점3-transformcomponent와-img-요소의-위치-불일치">문제점3. TransformComponent와 img 요소의 위치 불일치</h4>
<p>img 요소의 너비와 높이를 상태에 저장하고, TransformComponent의 width와 height 속성에 주입하는 방식으로 이 문제는 쉽게 해결할 수 있었다. img가 회전하면 TransformComponent의 width와 height 값을 반대로 설정해주면 된다.</p>
<p>문제는 이렇게 설정하면 img 요소와 TransformComponent의 위치가 미세하게 어긋나 다시 1번과 같은 문제가 발생한다는 점이었다.</p>
<p><img src="https://velog.velcdn.com/images/yo_onzy/post/6b1f0a3f-4db3-45c0-896b-8a497df83792/image.png" alt=""></p>
<p>이를 해결하기 위해 TransformComponent에 relative 값을 주고 img 요소에 absolute 값을 주어 강제로 중앙에 위치시켜 문제를 해결했다.</p>
<hr>
<h3 id="결론">결론</h3>
<h4 id="완성-코드">완성 코드</h4>
<pre><code>import { useEffect, useRef, useState } from &quot;react&quot;;
import { TransformWrapper, TransformComponent, ReactZoomPanPinchRef } from &quot;react-zoom-pan-pinch&quot;;

type ImageModalProps = {
  imageUrl: string;
};

export const ImageModal = ({ imageUrl }: ImageModalProps) =&gt; {
  const [imgRotateDeg, setImgRotateDeg] = useState(0); // 이미지 회전각도
  const [zoomScale, setZoomScale] = useState(1); // 현재 줌 배율
  const [contentDimensions, setContentDimensions] = useState&lt;{
    width: number;
    height: number;
  }&gt;({ width: 0, height: 0 });
  const [isImageEmpty, setIsImageEmpty] = useState(false); // 이미지 액박 확인
  const zoomInRef = useRef&lt;() =&gt; void&gt;();
  const zoomOutRef = useRef&lt;() =&gt; void&gt;();
  const resetTransformRef = useRef&lt;() =&gt; void&gt;();
  const imgRef = useRef&lt;HTMLImageElement&gt;(null); // img 요소 참조 생성
  const transformRef = useRef&lt;ReactZoomPanPinchRef | null&gt;(null);

  // 이미지 로드 시 크기 업데이트
  useEffect(() =&gt; {
    if (imgRef.current &amp;&amp; !isImageEmpty) {
      const { naturalWidth, naturalHeight } = imgRef.current;
      setContentDimensions({
        width: naturalWidth,
        height: naturalHeight,
      });
    }
  }, [imageUrl, imgRotateDeg, zoomScale]);

  // 줌 변경 시 호출되는 함수
  const handleZoomChange = (ref: ReactZoomPanPinchRef) =&gt; {
    setZoomScale(ref.state.scale);
  };

  const contentStyle: React.CSSProperties = {
    width:
      imgRotateDeg % 180 === 0 &amp;&amp; contentDimensions.width !== 0
        ? `${contentDimensions.width}px`
        : contentDimensions.height !== 0
        ? `${contentDimensions.height}px`
        : &quot;auto&quot;,
    height:
      imgRotateDeg % 180 === 0 &amp;&amp; contentDimensions.height !== 0
        ? `${contentDimensions.height}px`
        : contentDimensions.width !== 0
        ? `${contentDimensions.width}px`
        : &quot;auto&quot;,
    position: &quot;relative&quot;,
  };

  return (
    &lt;div style={{ width: &quot;100%&quot;, height: &quot;100%&quot;, display: &quot;flex&quot;, alignItems: &quot;center&quot;, justifyContent: &quot;center&quot; }}&gt;
      &lt;TransformWrapper
        initialScale={1}
        minScale={0.5}
        maxScale={10}
        centerOnInit
        onInit={(ref) =&gt; {
        // 줌 기능 버튼을 외부에 구현하기 위해 함수를 외부에 선언하고 주입함
          zoomInRef.current = ref.zoomIn;
          zoomOutRef.current = ref.zoomOut;
          resetTransformRef.current = ref.resetTransform;
          transformRef.current = ref;
        }}
        onZoom={(ref) =&gt; handleZoomChange(ref)}
        onZoomStop={(ref) =&gt; handleZoomChange(ref)}
      &gt;
        &lt;TransformComponent wrapperStyle={{ width: &quot;100%&quot;, height: &quot;100%&quot; }} contentStyle={contentStyle}&gt;
          &lt;img
            ref={imgRef}
            src={imageUrl}
            style={{
              position: &quot;absolute&quot;,
              top: &quot;50%&quot;,
              left: &quot;50%&quot;,
              transform: `translate(-50%, -50%) rotate(${imgRotateDeg}deg)`,
              transformOrigin: &quot;center center&quot;,
              transition: &quot;transform 200ms&quot;,
            }}
            onError={() =&gt; setIsImageEmpty(true)} // 액박 체크
          /&gt;
        &lt;/TransformComponent&gt;
      &lt;/TransformWrapper&gt;
      // 이미지 액박 시 예외처리
      {isImageEmpty &amp;&amp; &lt;div&gt;이미지를 불러올 수 없습니다.&lt;/div&gt;}
    &lt;/div&gt;
  );
};
</code></pre><p>img 요소의 너비와 높이를 contentDimensions에 초기화하고, 이미지 회전 상태(imgRotateDeg)에 따라 TransformComponent에 주입할 스타일 값을 contentStyle에 정의했다.</p>
<p>3번 문제를 해결하기 위해 img 요소는 TransformComponent에서 중앙에 정렬되도록 스타일 값을 추가했다.</p>
<p>이렇게 해서 이미지 회전 후 확대를 해도 TransformWrapper의 경계를 벗어나지 않고 잘림 없이 영역 내에서 상호작용할 수 있게 되었다.</p>
<p><img src="https://velog.velcdn.com/images/yo_onzy/post/2a6a6e95-8de4-4d3c-9f38-be631628dba9/image.gif" alt=""></p>
<hr>
<p>추가적으로 보완하고 싶은 부분은 이미지가 회전한 후 TransformComponent가 바로 중앙에 정렬되도록 하는 것이다. 이 부분은 적용 후에 포스팅을 업데이트할 예정이다.</p>
<p>끝!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Firebase 에러 - Uncaught (in promise) Error: Permission denied]]></title>
            <link>https://velog.io/@yo_onzy/Firebase-%EC%97%90%EB%9F%AC-Uncaught-in-promise-Error-Permission-denied</link>
            <guid>https://velog.io/@yo_onzy/Firebase-%EC%97%90%EB%9F%AC-Uncaught-in-promise-Error-Permission-denied</guid>
            <pubDate>Sun, 04 Jun 2023 05:28:33 GMT</pubDate>
            <description><![CDATA[<h2 id="에러-발견">에러 발견</h2>
<p>오랜만에 프로젝트 확인하려고 사이트에 들어갔더니 데이터를 가져오지 못하고 무한 로딩이 되고있었다 ,,</p>
<p>[에러 이미지]
<img src="https://velog.velcdn.com/images/yo_onzy/post/f88abd9b-9ec6-4ee1-ba36-1dc580188aac/image.png" alt=""></p>
<p>구글에 검색해보니 코드 문제는 아닌거같고 firebase문제인거 같아서 firebase에 들어가보니 알림이 와있었다 !</p>
<h2 id="firebase-보안규칙">Firebase 보안규칙</h2>
<p>[알림 이미지]
<img src="https://velog.velcdn.com/images/yo_onzy/post/d1abb47c-88fa-492e-bdbd-c9550ebb1cab/image.png" alt=""></p>
<p>보안 규칙을 업데이트하지 않으면 실시간 데이터베이스가 클라이언트 요청을 거부한다고 한다 ,, </p>
<p>해당 알림을 클릭하면 해결하는 방법을 알려주는 페이지로 이동된다.</p>
<p><a href="https://firebase.google.com/docs/rules/insecure-rules#database">firebase 보안규칙페이지</a></p>
<h2 id="해결">해결</h2>
<p>[firebase 페이지내 해결법]
<img src="https://velog.velcdn.com/images/yo_onzy/post/bc16438f-6fdc-4a97-933d-a4cb6c1c6bc5/image.png" alt=""></p>
<p>나는 해당 방법을 참고했다 !
오픈 액세스로 누구다 데이터를 수정, 삭제할 수 있도록 설정했다.</p>
<p>권장되지 않는 방법이라 써있긴 하지만 ,, </p>
<p>일단은 해결하는게 우선이니 오픈 액세스로 적용하였다.</p>
<p>페이지를 잘 읽어보면 firebase가 제안하는 안전한 규칙으로 설정할 수 있는듯 하다. 
시간나면 수정해봐야겠다.</p>
<p>아무튼 위의 내용대로 보안규칙을 수정했다.</p>
<p>[❗보안규칙 수정전]
<img src="https://velog.velcdn.com/images/yo_onzy/post/3314e461-1dbf-4d05-8383-0365b41b3dd2/image.png" alt="">
: 읽기 쓰기에 뭔가 적혀있는데 아마도 5/27 이후부터 데이터가 불러와지지 않은듯 하다. (지금은 6/4)</p>
<p>이 코드를 아래처럼 수정했다.</p>
<p>[✔️보안규칙 수정후]
<img src="https://velog.velcdn.com/images/yo_onzy/post/9fe7895c-dd35-4d9f-9e25-040f712ca740/image.png" alt="">
: 저장하니 경고메시지가 뜬다 ! 하지만 프로젝트는 이제 데이터가 잘 불러와진다.</p>
<hr>
<p>데이터를 보호하기 위한 Firebase 보안 규칙을 더 공부해서 적절한 규칙으로 수정이 필요해 보인다.</p>
<p>일단 급한 불만 끈걸로 만족 ..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[svg - ico로 변환하고 favicon 바꾸기 (안보이는 오류 해결)]]></title>
            <link>https://velog.io/@yo_onzy/svg-ico%EB%A1%9C-%EB%B3%80%ED%99%98%ED%95%98%EA%B3%A0-favicon-%EB%B0%94%EA%BE%B8%EA%B8%B0-%EC%95%88%EB%B3%B4%EC%9D%B4%EB%8A%94-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@yo_onzy/svg-ico%EB%A1%9C-%EB%B3%80%ED%99%98%ED%95%98%EA%B3%A0-favicon-%EB%B0%94%EA%BE%B8%EA%B8%B0-%EC%95%88%EB%B3%B4%EC%9D%B4%EB%8A%94-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Thu, 27 Apr 2023 16:10:01 GMT</pubDate>
            <description><![CDATA[<p>프로젝트에 원하는 svg(혹은 이미지 파일)을 ico로 변환하여 예쁜 favicon을 적용시켜보자 !</p>
<hr>
<h1 id="1-원하는-svg파일-저장하기">1. 원하는 svg파일 저장하기</h1>
<p>무료 다운로드 추천 사이트
<a href="https://www.flaticon.com/uicons/interface-icons">flaticon</a>
<a href="https://react-icons.github.io/react-icons/">react-icons</a></p>
<blockquote>
<p>원하는 svg가 파일은 없고 코드로만 존재한다면 메모장(혹은 vscode)에 붙여넣고 확장자를 .svg로 저장하면 됩니다.</p>
</blockquote>
<ul>
<li>svg파일 예시 
<img src="https://velog.velcdn.com/images/yo_onzy/post/1c872f9f-5d51-4c18-8336-8b7ee88d2669/image.png" alt=""></li>
</ul>
<p>fill속성에 원하는 색상코드를 입력할 수 있음</p>
<hr>
<h1 id="2-변환-사이트에서-파일-변환하기">2. 변환 사이트에서 파일 변환하기</h1>
<p>아무데서나 가능하지만 아래 사이트 추천
<a href="https://convertio.co/kr/svg-ico/">무료 변환 사이트</a></p>
<p>svg파일을 선택/드래그 앤 드롭하여 사이트에 올리고 ico파일로 변환한다.</p>
<p>변환된 파일을 다운로드받으면 끝 !</p>
<ul>
<li>변환된 파일 예시
<img src="https://velog.velcdn.com/images/yo_onzy/post/99356bfe-7f3c-4e2e-a62d-38922a8cbfbb/image.png" alt=""></li>
</ul>
<hr>
<h1 id="3-❗아무것도-안보이는-오류">3. ❗아무것도 안보이는 오류</h1>
<p>간혹 파일을 변환하여 다운로드 받았는데 ico파일을 열면 아무것도 보이지 않는 오류가 있다.</p>
<p>그럴때 변환전 svg파일의 width와 height속성을 512정도로 조절하면 정상적으로 잘 보인다.</p>
<hr>
<h1 id="4-favicon-적용하기-리액트">4. favicon 적용하기 (리액트)</h1>
<p>변환된 logo.ico파일을 public 경로에 저장한다.</p>
<p>index.html의 head태그 안에</p>
<pre><code class="language-js">&lt;link rel=&quot;icon&quot; href=&quot;%PUBLIC_URL%/icon.ico&quot; /&gt;</code></pre>
<p>위 코드 입력하면 적용 완료</p>
<p>적용된 모습
<img src="https://velog.velcdn.com/images/yo_onzy/post/7d343b46-79ff-4c20-9ef1-e20cad95a865/image.png" alt=""></p>
<hr>
<p>파일을 변환하는데 자꾸 아무것도 보이지 않는 오류가 발생했는데 해결법을 찾게되어 글로 정리하였다.</p>
<p>fill속성은 변경하기 전에 svg와 path중 어디에 입력해야 적용이 잘 되는지 미리 확인해보는것을 추천</p>
<p>만약 변환 후 아무것도 보이지 않는다면 svg파일의 크기를 늘려 다시 시도해보길 바란다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[개발비밀일기]]></title>
            <link>https://velog.io/@yo_onzy/%EA%B0%9C%EB%B0%9C%EB%B9%84%EB%B0%80%EC%9D%BC%EA%B8%B0</link>
            <guid>https://velog.io/@yo_onzy/%EA%B0%9C%EB%B0%9C%EB%B9%84%EB%B0%80%EC%9D%BC%EA%B8%B0</guid>
            <pubDate>Sat, 08 Apr 2023 16:17:27 GMT</pubDate>
            <description><![CDATA[<p>개발오답노트 썸네일용 ..
내용 없음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MongoDB Compass Error:
an error occurred while loading instance info: invalid utf-8 string in bson document 처리하기]]></title>
            <link>https://velog.io/@yo_onzy/MongoDB-Compass-Erroran-error-occurred-while-loading-instance-info-invalid-utf-8-string-in-bson-document-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@yo_onzy/MongoDB-Compass-Erroran-error-occurred-while-loading-instance-info-invalid-utf-8-string-in-bson-document-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 19 Mar 2023 14:15:15 GMT</pubDate>
            <description><![CDATA[<p>프로젝트를 시작하기도 전에 나를 괴롭혔던 몽고디비 에러 ... 
바로 ...</p>
<p><strong>MongoDB Compass Error:
an error occurred while loading instance info: invalid utf-8 string in bson document</strong></p>
<p><img src="https://velog.velcdn.com/images/yo_onzy/post/b534cbf2-c678-4d01-a57b-76e0fd539a6e/image.png" alt="">
이 에러다. </p>
<p>로컬 호스트에 연결이 안 되는 ..? 영알못이라 더 괴로웠다. </p>
<p>처음에는 해결법을 몰라서 구 버전을 다운로드해 사용했다.
홈페이지에서 구 버전을 찾을 수 없어서 아래 사이트를 참고했다.
<a href="https://stackoverflow.com/questions/69404825/mongodb-an-error-occurred-while-loading-navigation-invalid-utf-8-string-in-bs">참고 사이트 - 스택오버플로우</a></p>
<p><img src="https://velog.velcdn.com/images/yo_onzy/post/fb9427ac-af23-4931-8499-72b73c7265e4/image.png" alt="">
구 버전으로는 연결 됨 ..</p>
<p>위 사이트에서 compass 1.21.2버전을 다운로드했더니 연결이 돼서 일단 그냥 사용했는데
최신 버전의 몽고디비랑 호환되지 않는 부분이 있지 않을까 걱정이 됐다.</p>
<p>그래서 다시 구글링을 해보니
pc 이름에 한글, 특수문자, 공백이 들어가면 생길 수 있는 오류라는 것을 알았다!
(그때는 왜 못 찾았는지.. 참 .. )</p>
<p>아래는 pc 이름을 변경하는 방법이다.
(윈도우 11 기준)</p>
<hr>
<p><img src="https://velog.velcdn.com/images/yo_onzy/post/dffb7741-8383-4edf-83f8-c866603104e3/image.png" alt=""></p>
<ol>
<li>윈도우 시작 버튼 클릭해서 설정으로 들어가기</li>
</ol>
<p><img src="https://velog.velcdn.com/images/yo_onzy/post/91738012-584b-4f77-a412-30770ac0644e/image.png" alt="">
2. 파란 화살표 옆에 이름 바꾸기 클릭 (해당 부분에 설정된 이름이 pc 이름! pc 이름이 정여름)</p>
<p><img src="https://velog.velcdn.com/images/yo_onzy/post/875cd5a7-0634-4c16-a259-5fbf5b889fe1/image.png" alt="">
3. pc 이름을 영문과 숫자로만 입력 (한글, 특수문자, 공백 금지)</p>
<p><img src="https://velog.velcdn.com/images/yo_onzy/post/814422f6-6d8e-42a8-8bc6-421e296a8345/image.png" alt="">
4. 다시 시작하면 설정이 완료 (끝)</p>
<p>위처럼 pc 이름을 영문으로 변경하고 나면!</p>
<p><img src="https://velog.velcdn.com/images/yo_onzy/post/7b7cd976-becf-4cc1-842c-dc30c69c5f39/image.png" alt="">
이렇게 최신 버전에서도 잘 돌아간다.</p>
<hr>
<p>결론 : pc 이름에 한글, 특문, 공백이 들어가는지 확인하자!</p>
]]></description>
        </item>
    </channel>
</rss>