<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Bello.log</title>
        <link>https://velog.io/</link>
        <description>접근성과 UX, 데이터 표현에 관심이 많습니다.</description>
        <lastBuildDate>Sun, 18 Feb 2024 16:20:03 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Bello.log</title>
            <url>https://velog.velcdn.com/images/easy_dev/profile/6a04d51d-9b63-4d6c-906e-5d563dedd70a/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Bello.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/easy_dev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[접근성] tab key로 이동 시 modal 내 focus trap event]]></title>
            <link>https://velog.io/@easy_dev/%EC%A0%91%EA%B7%BC%EC%84%B1-tab-key%EB%A1%9C-%EC%9D%B4%EB%8F%99-%EC%8B%9C-modal-%EB%82%B4-focus-trap-event</link>
            <guid>https://velog.io/@easy_dev/%EC%A0%91%EA%B7%BC%EC%84%B1-tab-key%EB%A1%9C-%EC%9D%B4%EB%8F%99-%EC%8B%9C-modal-%EB%82%B4-focus-trap-event</guid>
            <pubDate>Sun, 18 Feb 2024 16:20:03 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p>tab key를 사용해 header 영역에 있는 검색 버튼을 클릭했을 때 검색 모달창이 정상적으로 나오지만, 모달창의 마지막 요소를 지나면 html 문서에서 모달창 뒤에 오는 요소로 tab이 이동하는 문제가 생겼다. 어디에 focus 되었는지, 검색창을 닫지도 않았는데 뒤에 오는 내용이 맥락없이 읽힐 수도 있다.</p>
<p><img src="https://velog.velcdn.com/images/easy_dev/post/60ec6dae-f75c-4645-9406-a0377e98ef49/image.png" alt="header 내 버튼"></p>
<h2 id="문제-해결">문제 해결</h2>
<h3 id="tabindex">tabindex?</h3>
<p>tabindex로 tab 순서를 강제로 지정해줄 수는 있다. 하지만 포커스 순서를 제어하는 것은 피하는 것이 좋다. 키보드 포커스가 순서대로 이동하는 것이 아닌 중구난방으로 이동하게 될 경우 혼란을 줄 수 있다. 마크업으로 문서에 요소를 순서에 맞게 배치하는 것이 훨씬 좋다.</p>
<p><a href="https://developer.mozilla.org/ko/docs/Web/HTML/Global_attributes/tabindex">tabindex - MDN 공식문서</a></p>
<h3 id="focus-trap">focus trap?</h3>
<p>현재 보이는 모달창 내에서 tab key가 이동할 수 있도록 하기 위해 focus trap을 적용하였다. esc를 누르면 모달창을 닫을 수 있고, 닫은 후에는 focus의 위치를 원래의 위치(검색 아이콘)에 두었다. shift + tab 으로 이동하더라도 모달창 내에서만 이동하도록 했다.</p>
<pre><code>function openSearchBox() {
  focusedElementModal = document.activeElement;

  // 포커스 가능한 요소들
  let focusableEls = $(searchBox).find(
    &#39;a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, *[tabindex], *[contentditable]&#39;,
  );
  focusableEls = Array.prototype.slice.call(focusableEls);
  searchBox.on(&#39;keydown&#39;, trapTabKey);

  const firstTabStop = focusableEls[0];
  const lastTabStop = focusableEls[focusableEls.length - 1];

  firstTabStop.focus();

  searchBox.addClass(&#39;active&#39;);
  $(&#39;body&#39;).addClass(&#39;notscroll&#39;);
  searchBox.attr(&#39;aria-hidden&#39;, &#39;false&#39;);

  // 포커스 트랩 이벤트
  function trapTabKey(e) {
    // check Tab key
    if (e.keyCode === 9) {
      if (document.activeElement === firstTabStop &amp;&amp; e.shiftKey) {
        e.preventDefault();
        lastTabStop.focus();
      } else if (document.activeElement === lastTabStop &amp;&amp; !e.shiftKey) {
        e.preventDefault();
        firstTabStop.focus();
      }
    }

    // ESCAPE
    if (e.keyCode === 27) {
      closeSearchBox();
    }
  }
}

// 검색창 숨기기
function closeSearchBox() {
  searchBox.removeClass(&#39;active&#39;);
  $(&#39;body&#39;).removeClass(&#39;notscroll&#39;);
  searchBox.attr(&#39;aria-hidden&#39;, &#39;true&#39;);
  focusedElementModal.focus();
}</code></pre><h2 id="마치며">마치며</h2>
<p>모달창 내의 포커스 이동을 통제할 수 있었지만, 검색 아이콘을 클릭하고 tab 키를 눌렀을 때 모달창의 첫 번째 요소로 이동하길 바랐으나 검색 아이콘과 형제 요소인 장바구니 아이콘으로 이동해버려서 이 부분은 추후 수정할 예정이다. 기본 뼈대인 마크업을 정확하게 해야 함을 매번 느끼지만 이번에도 다시금 느꼈다.</p>
<h4 id="참고-사이트">참고 사이트</h4>
<p><a href="https://lee-jing.tistory.com/19">https://lee-jing.tistory.com/19</a>
<a href="https://tarot-story.tistory.com/23">https://tarot-story.tistory.com/23</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[IT 용어]]></title>
            <link>https://velog.io/@easy_dev/IT-%EC%9A%A9%EC%96%B4</link>
            <guid>https://velog.io/@easy_dev/IT-%EC%9A%A9%EC%96%B4</guid>
            <pubDate>Fri, 02 Feb 2024 16:19:18 GMT</pubDate>
            <description><![CDATA[<h3 id="앱의-종류">앱의 종류</h3>
<h4 id="네이티브-앱native-app--모바일-기기에-최적화-된">네이티브 앱(Native app = 모바일 기기에 최적화 된)</h4>
<p>스마트폰 운영체제에 맞춰 개발된 앱. 안드로이드, iOS에서 사용하는 언어를 이용해 개발.</p>
<blockquote>
<p>장점</p>
</blockquote>
<ul>
<li>앱 실행 속도가 빠름</li>
<li>유용한 API 지원을 받을 수 있음</li>
<li>카메라, 페이스 인식, 연락처 조회 등 스마트폰 기본 기능들 활용 가능</li>
<li>각 운영 체제에 최적화되어 있어 안정적인 서비스 제공 가능</li>
<li>고성능 그래픽 처리 가능(2D, 3D, 증강현실 등)</li>
</ul>
<blockquote>
<p>단점</p>
</blockquote>
<ul>
<li>해당 플랫폼 별로 다른 언어를 사용함으로, 각 운영체제의 개발 언어를 알고 있어야 한다.</li>
<li>플레이 스토어 &amp; 앱 스토어에 올려 심사를 받고 통과해야만 앱이 등록되기 때문에 번거로움</li>
<li>앱 업데이트 시 심사를 거쳐야 하기 때문에 업데이트 또한 번거로움</li>
<li>각 운영체제 별로 개발해야 하므로 개발 비용 발생</li>
</ul>
<h4 id="웹뷰webview">웹뷰(WebView)</h4>
<p>네이티브 앱 내에 내제되어 있는 웹 브라우저이다. 앱을 벗어나지 않고도 웹 브라우저를 띄울 수 있다. 프론트엔드 개발자가 작성한 코드가 앱 내의 웹뷰 컴포넌트로 임베딩된다.</p>
<blockquote>
<p>웹뷰를 사용하는 이유?</p>
</blockquote>
<ul>
<li>네이티브 앱 개발의 단점을 커버치기 위해 사용.<ul>
<li>앱 스토어 리스크 : 앱은 스토어를 통한 배포 프로세스이기 때문에 개발, 빌드 후 배포를 하더라도 바로 사용자에게 전달되는 것이 아니다. 각 스토어마다 앱의 가이드라인이 다르고 추후 정책이 바뀔 수 있기 때문에 리스크가 있음.</li>
<li>버전 이슈 : 앱 최신 버전이 업데이트 되면 사용자들은 앱 버전 업데이트를 해줘야 하는데 자주 업뎃이 되면 사용자의 사용성이 떨어질 수 있다. </li>
<li>개발자 풀 : 앱개발자들은 실제로 많이 없고, 회사에서도 잘 뽑지 않는다. 네이티브 앱 만으로 만들어진 거대한 서비스는 없다고 한다.</li>
</ul>
</li>
<li>앱을 벗어나지 않는다는 점이 포인트. 유저들을 앱을 벗어나 다른 컨텐츠가 뜨게 되면 방금까지 사용하던 앱으로 돌아갈 확률이 감소하게 된다고 한다.</li>
<li>잘만든 웹뷰 화면들은 네이티브 앱인지 웹인지 분간이 어렵기 때문에 잘 만들면 서비스적 측면에서 더 나은 선택지가 될 수 있음.</li>
<li>더 많은 기능을 빠르게 제공(핵심!): 사용자들에게 기능들을 빠르게 제공하고 수정할 수 있다. 유저들의 피드백을 수용해 즉각적으로 반영하여 빠르게 서비스를 개선해 나갈 수 있다.</li>
</ul>
<h4 id="웹뷰가-일반-웹과-달라야-하는-점">웹뷰가 일반 웹과 달라야 하는 점</h4>
<p>웹 개발자가 일반적으로 개발하는 일반 웹과 네이티브 앱에 들어가는 웹뷰는 개발 성격이 살짝 다르다. 앱은 화면과 화면 간의 전환이 정말 빠르고 자연스럽게 이뤄지므로 프레임이나 렌더링 성능이 좋아야 한다. 만약 웹뷰로 만들어진 페이지에서 화면 전환 시 렌더링이 늦어진다면 사용자 경험은 떨어진다. </p>
<blockquote>
<p>웹뷰는 빨라야 한다. 그러므로 웹뷰는 일반 웹과 달리 다른 성능 지표를 생각하며 개발해야 한다.</p>
</blockquote>
<ol>
<li>LCP(Largest Contentful Paint) : FCP(First Contentful Paint)와는 살짝 다르게 페이지의 가장 큰 컨텐츠 = 페이지의 주요 내용이 얼마나 빠르게 렌더링 되는지에 대한 지표</li>
<li>CLS(Cumulative Layout Shift) : 레이아웃이 얼마나 덜 끊기는지에 대한 지표. 컨텐츠를 보다가 갑자기 이미지가 나중에 렌더링되서 뚝 끊기는 현상을 줄여야 한다.</li>
<li>FID(First Input Delay) : 사용자와 인터랙션(터치, 드래그 등등)에 따라서 얼마나 빠르게 반응하는지를 요구하는 지표.</li>
</ol>
<p><a href="https://kokyusik91.github.io/story/ninth/">출처</a></p>
<h4 id="크로스-플랫폼-프레임워크cross-platform-frameworks">크로스 플랫폼 프레임워크(cross-platform frameworks)</h4>
<p>한 가지 개발 언어와 프레임워크로 ios, 안드로이드 양쪽 스토어에 출시할 수 있는 개발 방법. 둘 이상의 플랫폼에서 돌아가는 앱을 만들 수 있다.</p>
<p>예) 구글 Flutter, 페이스북의 React Native</p>
<blockquote>
<p>장점</p>
</blockquote>
<ul>
<li>코드를 하나만 작성하면 양쪽 스토어에서 모두 사용할 수 있어 시간을 줄 일 수 있음</li>
<li>빠른 개발 가능</li>
<li>하나의 코드로 작성하기 때문에 유지보수 용이</li>
</ul>
<blockquote>
<p>단점</p>
</blockquote>
<ul>
<li>기존 네이티브 앱이 가지고 있는 동적인 요소 퍼포먼스를 100% 구현할 수 없음</li>
<li>네이티브 앱에 비해 성능이 떨어져 느린 경우가 있음</li>
<li>라이브러리가 다양하지 않아 기능 구현에 어려움이 있을 수 있음</li>
<li>업데이트 지연 발생 가능</li>
</ul>
<h4 id="모바일-웹-앱">모바일 웹 앱</h4>
<p>네이티브 앱처럼 보이고, 기능 또한 앱과 동일하게 구현되지만 웹 기술을 활용하여 만들어진 앱을 &#39;웹앱&#39;이라 한다. 네이티브 앱과 달리 웹 기반의 html, css, js 등을 활용하며, 별도의 앱 파일을 설치하지 않고 인터넷 브라우저 기반으로 작동된다.</p>
<p>앱 없이 인터넷을 통해 구동되는 것을 보면 일반 모바일 웹과 똑같은 것이라 느낄 수 있지만, 두 가지의 차이가 약간 있다. </p>
<blockquote>
<ul>
<li>모바일 웹 : PC를 기준으로 제작된 뒤 모바일 화면 규격에 맞게 폰트나 이미지 등을 바꾼 것.</li>
</ul>
</blockquote>
<ul>
<li>웹 앱 : 처음부터 모바일을 기준으로 제작되어 이용자에게 훨씬 편안한 환경을 제공한다. 하지만 설치하는 앱이 아니므로 카메라나 음성 등 디바이스 자체 기능에 접근할 수는 없다. </li>
</ul>
<p>++ 차이점
실행 방식에 차이가 있다. 모바일 웹은 화면의 일부분이 바뀔 때 전체를 서버에서 새롭게 불러오는 풀 브라우저방식, 웹앱은 변경이 필요한 부분만 바꾸는 단일 페이지 방식(Single page application)을 사용. 따라서 모바일 웹이 웹앱보다 속도가 느리다는 특징이 있음.</p>
<blockquote>
<ul>
<li>웹앱 : 모바일 화면도 구성한 웹</li>
</ul>
</blockquote>
<ul>
<li>모바일 앱 : 모바일 화면만 구성한 앱</li>
</ul>
<p>++ 차이점
<img src="https://velog.velcdn.com/images/easy_dev/post/3d1e2181-f406-47d6-a9f3-ebed2ac78b37/image.png" alt=""></p>
<p><a href="https://youtu.be/hsh8BS7gyrY">인프콘 웹뷰 영상</a></p>
<h4 id="하이브리드-앱hybrid-app">하이브리드 앱(Hybrid app)</h4>
<p>웹과 앱의 장점을 하나로 합침. 하이브리드 앱은 네이티브 앱 + 웹앱 개발 방식을 모두 사용한다. 앱의 화면이나 기능 등 콘텐츠 영역은 웹 기반으로 제작하고 겉모습은 앱 마켓 등록과 설치를 위해 네이티브 앱으로 포장함. 두 가지 개발 방식을 이용하므로 웹과 앱의 API 모두 사용 가능하다. 따라서 웹앱에서는 불가능 했던 디바이스 자체 기능에 접근할 수 있게 된다.</p>
<blockquote>
<p>장점</p>
</blockquote>
<ul>
<li>웹 기술을 기반으로 제작되지만, 모바일 API도 사용할 수 있으므로 디바이스 자체 기능을 활용할 수 있음</li>
<li>네이티브 앱에 비해 개발 비용 및 시간 절약 가능</li>
<li>한 번 개발해두면 패키징을 바꾸는 방식으로 여러 플랫폼에 대응 가능</li>
</ul>
<blockquote>
<p>단점</p>
</blockquote>
<ul>
<li>네이티브 앱 개발 지식 필요</li>
<li>브라우저의 성능이 떨어지면 앱 구동 속도 저하됨</li>
<li>네이티브 앱에 비해 디자인의 자유도가 떨어짐</li>
</ul>
<h3 id="sdk">SDK</h3>
<p>software development kit. 어떤 소프트웨어를 만들기 위한 도구 모음. 그 도구 안엔 API도 있을 수 있다.</p>
<p>예) ios SDK를 다운받으면 개발자에게 ios 어플리케이션을 만드는데 필요한 모든 도구가 제공된다. 아이폰 시뮬레이션을 할 수 있는 시뮬레이터도 제공됨.</p>
<h3 id="api">API</h3>
<p>application programming interface. 모듈화되어 만들어진 것으로, 어떤 기능을 제어하거나 제공하는 인터페이스를 말함. 대부분의 어플리케이션은 API에 의존하고 있다.</p>
<p>예) 업로드 버튼을 클릭하면 업로드 된다거나, 
계산기를 이용해 값을 얻을 수 있는 것.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹표준, 웹접근성, 크로스브라우징 +]]></title>
            <link>https://velog.io/@easy_dev/%EC%9B%B9%ED%91%9C%EC%A4%80-%EC%9B%B9%EC%A0%91%EA%B7%BC%EC%84%B1-%ED%81%AC%EB%A1%9C%EC%8A%A4%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A7%95-r48kw02s</link>
            <guid>https://velog.io/@easy_dev/%EC%9B%B9%ED%91%9C%EC%A4%80-%EC%9B%B9%EC%A0%91%EA%B7%BC%EC%84%B1-%ED%81%AC%EB%A1%9C%EC%8A%A4%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A7%95-r48kw02s</guid>
            <pubDate>Fri, 29 Dec 2023 16:53:09 GMT</pubDate>
            <description><![CDATA[<h2 id="웹표준">웹표준</h2>
<ol>
<li><p>HTML5
<a href="https://validator.w3.org/">W3C(Markup Validation Service)</a></p>
</li>
<li><p>CSS
<a href="https://css-validator.org/">W3C(CSS Validation Service)</a></p>
</li>
</ol>
<h2 id="web-accessibility">Web Accessibility</h2>
<p>1.한국형 웹 콘텐츠 접근성 지침
[웹 접근성 연구소](<a href="https://www.wah.or.kr:444/Participation/guide.asp">한국형 웹 콘텐츠 접근성 지침 2.2</a>)</p>
<ol start="2">
<li><p>OpenWAX
<a href="https://chromewebstore.google.com/detail/openwax/bfahpbmaknaeohgdklfbobogpdngngoe">OpenWAX(Open Web Accessibility eXtension)</a></p>
</li>
<li><p>WAVE
<a href="https://wave.webaim.org/">WAVE Web Accessibility Evaluation Tools</a></p>
</li>
<li><p>sitero(명도 대비 검사)
<a href="https://sitero.co.kr/contrast">sitero</a></p>
</li>
</ol>
<h2 id="웹호환성">웹호환성</h2>
<p><a href="https://caniuse.com/">Can I use</a></p>
<h2 id="cross-browsing">Cross Browsing</h2>
<p><a href="https://www.browserstack.com/">Browserstack</a></p>
<h2 id="lighthouse">Lighthouse</h2>
<p>구글에서 만든 웹페이지의 품질 개선을 위한 오픈소스 자동화 도구. 아래 5가지의 항목별 지표를 확인할 수 있으며 웹사이트의 성능 개선을 위한 지침도 함께 제공한다.</p>
<ul>
<li>퍼포먼스(성능)</li>
<li>접근성</li>
<li>웹표준</li>
<li>SEO</li>
<li>PWA</li>
</ul>
<p><a href="https://chromewebstore.google.com/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk?hl=ko">Lighthouse</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[🍞] html]]></title>
            <link>https://velog.io/@easy_dev/html-markup</link>
            <guid>https://velog.io/@easy_dev/html-markup</guid>
            <pubDate>Sat, 18 Nov 2023 15:36:31 GMT</pubDate>
            <description><![CDATA[<h2 id="img-태그의-alt-속성">img 태그의 alt 속성</h2>
<blockquote>
<ul>
<li>내용을 알 수 없는 이미지</li>
</ul>
</blockquote>
<ul>
<li>이미지를 볼 수 있는 특정인과의 사적인 문서(사이트, 이메일)에 사용된 이미지</li>
<li>장식 이미지에 alt=&quot;&quot; 대신 role=&quot;presentation&quot; 사용</li>
</ul>
<p>위와 같은 특별한 경우를 제외하고 거의 모든 경우에 alt 속성을 사용한다.</p>
<p><a href="https://velog.io/@eunsoo0504/img%ED%83%9C%EA%B7%B8-alt-%EC%86%8D%EC%84%B1-%EC%8D%A8%EC%95%BC%ED%95%A0%EA%B9%8C#2-alt%EC%86%8D%EC%84%B1%EC%9D%84-%EC%A0%81%EC%A7%80-%EC%95%8A%EB%8A%94-%ED%8A%B9%EB%B3%84%ED%95%9C-%EA%B2%BD%EC%9A%B0">출처 - img태그 alt 속성 써야할까?</a></p>
<h2 id="heading과-sectioning-elements">Heading과 Sectioning elements</h2>
<h3 id="heading-elements-h1-h6">Heading elements <code>&lt;h1&gt;-&lt;h6&gt;</code></h3>
<blockquote>
<ul>
<li>폰트 사이즈를 이유로 태그를 사용하지 말고, 의미있는 태그로 사용할 것.</li>
</ul>
</blockquote>
<ul>
<li><code>&lt;h1&gt;</code>, <code>&lt;h2&gt;</code>, <code>&lt;h3&gt;</code> ... 순차적으로 사용할 것.</li>
<li>페이지 당 하나의 h1만 사용할 것을 권장. 여러 <code>&lt;h1&gt;</code> 사용이 가능하긴 하지만 접근성과 SEO를 위해 되도록이면 <code>&lt;h1&gt;</code> 하나만 사용.</li>
</ul>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements">출처 : MDN) The HTML Section Heading elements</a></p>
<h3 id="sectioning-elements">Sectioning elements</h3>
<blockquote>
<p><code>&lt;header&gt;</code>, <code>&lt;footer&gt;</code>, <code>&lt;main&gt;</code>, <code>&lt;article&gt;</code>, <code>&lt;section&gt;</code>, <code>&lt;nav&gt;</code>, <code>&lt;aside&gt;</code></p>
</blockquote>
<p>위의 section 요소들에는 반드시 heading tag가 포함되어야 한다. 위치에 상관없이 section 요소 내에 포함하기만 하면 되는 것 같다.</p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section">출처 : MDN) section content</a></p>
<h2 id="svg">svg</h2>
<blockquote>
<p>SVG란?
: Scalable Vector Graphics. XML 기반의 2차원 벡터 그래픽으로 HTML 요소로 이뤄져 있어 CSS와 Javascript로 컨트롤이 가능하다. 벡터이기 때문에 확대하거나 크기를 키워도 이미지가 깨지지 않으며 용량도 늘어나지 않는다.</p>
</blockquote>
<p>다만, 그래픽을 수학적 연산으로 그려낸 이미지이기 때문에 복잡한 형태일수록 사이즈가 커질 수 있다. 단순한 아이콘이나 로고, 도형을 구현할 때 사용된다.</p>
<h3 id="svg-사용법">svg 사용법</h3>
<blockquote>
<p>svg 코드를 직접 수정하여 이미지를 컨트롤 해야할 경우 - 3, 4번
이미지를 직접 컨트롤 하지 않을 경우 - 1, 2번</p>
</blockquote>
<ol>
<li><code>img</code> 요소의 src에 이미지 경로로 작성하는 방법</li>
<li>css background 이미지로 넣는 방법</li>
<li><code>svg</code> 코드를 복붙하여 HTML 안에 넣는 방법</li>
<li><code>object</code> 요소 안에 넣는 방법</li>
</ol>
<h2 id="스크린-리더와-관련된-속성과-태그">스크린 리더와 관련된 속성과 태그</h2>
<h3 id="aria-hidden">aria-hidden</h3>
<blockquote>
</blockquote>
<ul>
<li>웹 접근성을 개선하기 위해 사용되는 속성으로, 해당 요소의 시각적인 표현 여부를 스크린 리더 등 보조 기술에 전달함.</li>
<li>true/false의 속성값을 가짐.</li>
</ul>
<pre><code>aria-hidden=&quot;true&quot; // 스크린 리더가 읽지 않음
aria-hidden=&quot;false&quot;  // 스크린 리더가 읽음</code></pre><p>true는 다음과 같은 경우 추가하면 안된다고 mdn 사이트에 나와있다.</p>
<ul>
<li>html hidden 속성이 존재하는 경우</li>
<li>요소 또는 요소의 조상이 display: none 으로 숨겨진 경우</li>
<li>요소 또는 요소의 조상이 visibility: hidden 으로 숨겨진 경우</li>
</ul>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-hidden">출처 : MDN) aria-hidden</a></p>
<h3 id="display-none">display: none</h3>
<table>
<thead>
<tr>
<th>속성</th>
<th>화면</th>
<th>스크린 리더</th>
</tr>
</thead>
<tbody><tr>
<td>display: none</td>
<td>X</td>
<td>O</td>
</tr>
<tr>
<td>aria-hidden=&quot;true&quot;</td>
<td>O</td>
<td>X</td>
</tr>
<tr>
<td>display: none + aria-hidden=&quot;true&quot;</td>
<td>X</td>
<td>X</td>
</tr>
</tbody></table>
<h3 id="i-태그">i 태그</h3>
<blockquote>
<p>i 태그 자체는 스크린 리더에서 읽히지 않는다. i 태그는 주로 아이콘 같은 비의미적인 스타일을 적용하기 위해 사용되지만, 스크린 리더는 이런 스타일을 무시하고 의미 있는 컨텐츠만 읽는다.</p>
</blockquote>
<pre><code>&lt;i class=&quot;icon&quot;&gt;🔍&lt;/i&gt;</code></pre><p>위와 같이 유니코드 이모지를 사용하는 경우, 스크린 리더는 일반적으로 이모지의 유니코드를 읽지 않고, 대신에 브라우저에 내장된 기본 음성 표현을 사용해 해당 이모지를 설명한다.</p>
<p>따라서 🔍 는 스크린 리더에게 돋보기 또는 관련된 용어로 읽힌다고 한다.</p>
<p>만약 정확한 아이콘의 의미를 전달하고 싶다면, 적절한 대체 텍스트나 ARIA 속성을 사용하면 된다.</p>
<pre><code>role 속성 사용:
&lt;i class=&quot;ic-cart&quot; role=&quot;img&quot; aria-label=&quot;장바구니 아이콘&quot;&gt;&lt;/i&gt;

aria-label 속성 사용:
&lt;i class=&quot;icon&quot; aria-label=&quot;Search&quot;&gt;&lt;/i&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[🍞] figma shortcut key]]></title>
            <link>https://velog.io/@easy_dev/figma-shortcut-key</link>
            <guid>https://velog.io/@easy_dev/figma-shortcut-key</guid>
            <pubDate>Sun, 22 Oct 2023 15:31:42 GMT</pubDate>
            <description><![CDATA[<h4 id="피그마-전체-단축키-확인">피그마 전체 단축키 확인</h4>
<p>OS : ctrl + shift + /
Windows : cmd + shift + /</p>
<h4 id="전체-피그마-ui-표시숨기기">전체 피그마 UI 표시/숨기기</h4>
<p>OS : cmd + <br>Windows : ctrl + \</p>
<h4 id="이름-한번에-변경">이름 한번에 변경</h4>
<p>OS : cmd + R
Windows : ctrl + R</p>
<p>요소 다중 선택 후 Rename + $NN(오름차순 넘버)/$nn(내림차순 넘버)</p>
<h4 id="quick-actions">Quick actions</h4>
<p>ctrl + /</p>
<h4 id="layout-showhide">layout show/hide</h4>
<p>shift + G</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[🍞]css]]></title>
            <link>https://velog.io/@easy_dev/css</link>
            <guid>https://velog.io/@easy_dev/css</guid>
            <pubDate>Tue, 17 Oct 2023 15:55:17 GMT</pubDate>
            <description><![CDATA[<h2 id="1-postionfixed로-변경-후-스크롤-시-점프-동작">1. postion:fixed로 변경 후 스크롤 시 점프 동작</h2>
<p>최상단에 nav와 로고 등이 있는 header 영역(바로 위에는 이벤트 배너가 있는 구조)이 position:relative 값을 먹고 있고, 아래로 스크롤 했을 때 fixed로 변경해서 고정해야 하는데 스크롤 시 부자연스럽게(띡박거림) 상단에 fix 되었다. 스무스하게 header가 제자리에 챡- 붙었으면 좋겠어서 찾아봤다. </p>
<h4 id="컨텐트-영역의-위치-간격주기">컨텐트 영역의 위치 간격주기</h4>
<p>header 영역은 그대로 두고, content 영역에 header의 height 만큼의 top 위치를 줘서 스크롤 되면서 header가 점프하듯이 기존의 위치에서 최상단에 fix될 때, 그 사이 간격을 띄우면 자연스럽게 붙게 된다.</p>
<p>content 영역의 위치를 변경하는 것이기 때문에 기존의 content의 위치에 따라 레이아웃에 영향을 줄 수 있다.</p>
<p><a href="https://stackoverflow.com/questions/41678751/smooth-scroll-header-with-fixed-position">stackoverflow) Smooth scroll header with fixed position</a></p>
<h2 id="2-font-smooth">2. font-smooth</h2>
<pre><code>  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;</code></pre><p>웹폰트를 부드럽게 사용하기 위해 css의 font-smoothing 속성을 사용. 이 속성을 사용하면 브라우저에 표현되는 텍스트가 렌더링 될 때 좀 더 부드럽게 표현이 된다. 표준 속성은 아니라서 모든 브라우저 및 환경에 적용되지는 않는다. Safari 및 Mac 등에서는 완벽하게 구현되지만 이외에는 제한적으로 사용된다.</p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/font-smooth">출처 - MDN) font-smooth</a></p>
<h2 id="3-scroll-chanining">3. scroll chanining</h2>
<blockquote>
<p>사이드바를 만들고 스크롤을 할 때 사이드바가 전부 스크롤 되면 메인 화면이 스크롤이 된다. 왜 메인 화면까지 스크롤이 되냐하면, 스크롤이 되지 않는 부분에서 스크롤을 했을 때 부모 요소로 스크롤이 전달되기 때문이라고 한다. 이런 현상을 스크롤 체이닝(scroll chaining)이라 한다.</p>
</blockquote>
<p>이러한 현상을 해결하기 위한 css의 <code>&lt;overscroll-behavior&gt;</code> 속성이 있다.</p>
<h3 id="overscroll-behavior">overscroll-behavior</h3>
<p>스크롤이 끝까지 갔을 때 스크롤이 전달되는 것을 막는 속성이다.</p>
<pre><code>overscroll-behavior: auto | contain | none;</code></pre><p><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior">출처 - MDN) overscroll-behavior</a>
<a href="https://junghyeonsu.com/posts/overscroll-behavior/">출처 - 정현수님 스크롤체이닝 포스트</a></p>
<h3 id="scrolling-막기-css--js">scrolling 막기 (css + js)</h3>
<p>모달창을 띄워서 뒤의 컨텐트의 스크롤을 막거나 사이드바+오버레이로 반쯤 뒤에 가려진 컨텐트의 스크롤을 막는 방법</p>
<h4 id="1-모달창-스크롤-막기">1. 모달창 스크롤 막기</h4>
<pre><code>  overflow: hidden;
  touch-action: none;</code></pre><p>클래스명을 지정해주고 javascript에서 모달창을 열었을 때, body 부분에 클래스를 준다.</p>
<pre><code>  document.body.classList.add(&#39;notscroll&#39;);</code></pre><h4 id="2-사이드바-스크롤-막기">2. 사이드바 스크롤 막기</h4>
<pre><code>  position: fixed;
  top: 0;
  left: 0;
  width: 100%;</code></pre><p>위와 마찬가지로 사이드바를 열었을 때, body 부분에 클래스를 주면 된다.</p>
<hr>

<h2 id="4-truncate">4. truncate</h2>
<pre><code> white-space: nowrap;
 overflow: hidden;
 text-overflow: ellipsis;</code></pre><p>텍스트가 길어서 부모 요소의 너비 크기를 벗어나 바깥으로 튀어나가는 한 줄인데 부모 너비 밖의 잘린 텍스트는 숨기고 말 줄임표를 붙여야 할 때 위의 코드를 사용.</p>
<p><a href="https://www.daleseo.com/css-white-space/">출처 - Dales</a></p>
<hr>

<h2 id="5-transition과-css-property">5. transition과 css property</h2>
<blockquote>
<p>요소에 움직임을 주고 싶을 때 간혹 transition이 먹히지 않는 경우가 있다. 모든 css property가 대상이 되지는 않기 때문이다. 그 이유는 브라우저 동작과 관련 되어 있다.</p>
</blockquote>
<h3 id="대상이-될-수-있는-css-property">대상이 될 수 있는 css property</h3>
<pre><code>// Box model
width height max-width max-height min-width min-height
padding margin
border-color border-width border-spacing

// Background
background-color background-position

// 좌표
top left right bottom

// 텍스트
color font-size font-weight letter-spacing line-height
text-indent text-shadow vertical-align word-spacing

// 기타
opacity outline-color outline-offset outline-width
visibility z-index</code></pre><p><a href="https://poiemaweb.com/css3-transition">출처 - 이웅모님 Poiemaweb) css3-transition</a></p>
<hr>

<h2 id="6-visibility와-display-차이">6. visibility와 display 차이</h2>
<blockquote>
<p>요소를 화면에 보이지 않게 하기 위해 위의 속성을 자주 사용하게 되는데, 단순히 보이고 안 보이고의 차이만 있는 것은 아니다. 요소가 차지하고 있던 공간을 그대로 유지하느냐의 차이가 있다.</p>
</blockquote>
<pre><code>visibility: hidden | visible | collapse;</code></pre><p><code>&lt;display: none&gt;</code> 을 줄 경우, 그 요소가 차지하고 있던 공간도 함께 숨겨지게 된다. 그 다음으로 오는 요소가 숨겨진 요소의 자리를 대신 차지하게 된다.</p>
<p><code>&lt;visibility: hidden&gt;</code> 을 줄 경우, 요소가 차지하고 있던 공간을 그대로 유지한 채 숨김 처리 된다.</p>
<p><a href="https://developer.mozilla.org/ko/docs/Web/CSS/visibility">출처 - MDN) visibility</a></p>
<hr>

<h2 id="7-overflow">7. overflow</h2>
<blockquote>
<ul>
<li>내부 요소가 부모의 범위를 벗어날 경우 어떻게 처리할지 지정하는 속성.</li>
</ul>
</blockquote>
<ul>
<li>속성이 효력을 갖기 위해서는 반드시 block level의
width나 height의 값을 설정해줘야 한다.</li>
</ul>
<p><a href="https://developer.mozilla.org/ko/docs/Web/CSS/overflow">출처 - MDN) overflow</a></p>
<h3 id="2-display--none">2. display : none</h3>
<blockquote>
<p>요소를 보이지 않게 하는 속성. 화면에 보이지 않을 뿐만 아니라 스크린리더도 읽지 못함.</p>
</blockquote>
<h2 id="overflowhidden-displaynone의-차이">overflow:hidden, display:none의 차이</h2>
<p>overflow:hidden일 경우 -&gt; 숨김처리, 스크린리더가 읽을 수 있음.
display:none일 경우 -&gt; 숨김처리, 스크린리더가 읽을 수 없음.</p>
<hr>

<h2 id="8-margin-0-auto-가운데-정렬-안되는-이유">8. margin: 0 auto 가운데 정렬 안되는 이유</h2>
<h3 id="1-width-선언-여부">1. width 선언 여부</h3>
<p>요소의 width를 선언하지 않았을 경우, <code>margin: 0 auto;</code>가 작동하지 않는다. 만약 width를 선언하고 남은 공간이 있다면 margin으로 자동으로 채우게 되는데,
<img src="https://velog.velcdn.com/images/easy_dev/post/7be0a4e0-92d3-421e-b420-f27ada957bb9/image.png" alt="margin auto 예시"></p>
<p><code>left</code>, <code>right</code> 를 auto로 하게 되면 margin을 사이좋게 나눠가지게 되면서 가운데 정렬 된다.
부모 요소에 <code>margin: 0 auto;</code>를 줬는데 가운데 배치가 되지 않는다면 부모 요소의 width를 선언했는지 확인한다.</p>
<h3 id="2-box-type이-inline-일-경우">2. box-type이 inline 일 경우</h3>
<p><code>display: inline;</code>은 <code>margin-left, margin-right</code>이 먹히지 않는다. </p>
<h3 id="3-li-안에-a-태그-클릭-영역">3. li 안에 a 태그 클릭 영역</h3>
<p>메뉴 컴포넌트 만들 때 li 태그 안에 a 태그를 넣어서 사용하는 경우가 많은데, padding을 줘야 할 때 li에 주지 않고 a 태그에 줘야 클릭 영역이 넓어져서 빈 공간을 클릭했을 때에도 link 이동이 가능해진다.</p>
<hr>

<h2 id="9-z-index">9. z-index</h2>
<p>Position된 요소들의 수직방향으로의 레벨을 알려주는 속성.
레이어층으로 이뤄진 요소들을 어느 레벨로 위치시킬건지 정한다.
정수값을 적어주면 되는데, 정수값이 커질수록 상위 레벨에 속한다.</p>
<h2 id="10-img-태그">10. img 태그</h2>
<p>img는 inline box-type이지만 width와 height 값을 주면 사이즈 조절이 된다. 아마 이미지 파일 자체가 가지고 있는 이미지 사이즈가 있기 때문에 그럴텐데, 명시적으로 display:block을 해주면 스타일 오차를 줄일 수 있다. block으로 바꿔주면 개발자도구의 box-model 패널을 보면 content 부분의 값이 지정해준 값으로 딱 맞춰진다.</p>
<h2 id="11-text-align">11. text-align</h2>
<p>block 내에 있는 inline 요소(텍스트 포함)을 정렬할 때 사용하는 속성.
span이나 strong과 같은 inline 태그에 text-align 속성을 주고 싶다면 display:block 을 주면 된다. </p>
<h2 id="13-text-hidden">13. text hidden</h2>
<pre><code>font-size: 0;
overflow: hidden;
text-indent: 100%;
white-space: nowrap;</code></pre><pre><code>text-indent: -9999em;</code></pre><h2 id="14-position--sticky">14. position : sticky</h2>
<p>sticky로 고정된 첫번째 요소가 스크롤 위치에 따라 상단에 고정(sticky)된 상태에서도, 뒤에 오는 형제 요소들은 첫번째 요소가 원래 자리한 위치를 인식한다. 그래서 스크롤에 따라 뷰포트 내에서 고정되는 것처럼 보이지만, 레이아웃 상에서는 여전히 원래 자리의 공간을 차지하고 있기 때문에 바로 뒤에 오는 형제 요소들이 바로 이어져 보이지 않고 약간의 공백이 보이게 된다.</p>
<h2 id="15-aspect-ratio가-작동되지-않는-이유">15. aspect-ratio가 작동되지 않는 이유</h2>
<p><a href="https://codingeverybody.kr/css-aspect-ratio-%EC%86%8D%EC%84%B1/">aspect-ratio</a> </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Netlify로 배포 시 Proxy 에러 (naver open api)]]></title>
            <link>https://velog.io/@easy_dev/Netlify%EB%A1%9C-%EB%B0%B0%ED%8F%AC-%EC%8B%9C-Proxy-%EC%97%90%EB%9F%AC-naver-open-api</link>
            <guid>https://velog.io/@easy_dev/Netlify%EB%A1%9C-%EB%B0%B0%ED%8F%AC-%EC%8B%9C-Proxy-%EC%97%90%EB%9F%AC-naver-open-api</guid>
            <pubDate>Tue, 19 Sep 2023 14:57:11 GMT</pubDate>
            <description><![CDATA[<h2 id="cors-에러">CORS 에러</h2>
<pre><code class="language-jsx">npm i http-proxy-middleware</code></pre>
<p>src/ setupProxy.js
src 폴더 안에 setupProxy.js 을 두고</p>
<pre><code class="language-jsx">const { createProxyMiddleware } = require(&#39;http-proxy-middleware&#39;);

module.exports = function (app) {
   app.use(
      createProxyMiddleware(&#39;/v1/search/book.json&#39;, {
         target: &#39;https://openapi.naver.com&#39;,
         changeOrigin: true,
      }),
   );
   app.use(
      createProxyMiddleware(&#39;/api&#39;, {
         target: &#39;http://localhost:3000/&#39;,
         changeOrigin: true,
      }),
   );
};</code></pre>
<p>위와 같이 설정해줬다. 사용할 api 가 여럿인 경우 위처럼 사용하면 된다.
404 에러코드는 주소 설정이 잘못된 것이고, 400인 경우 쿼리를 넘겨주면 제대로 받아온다.</p>
<h3 id="참고-링크">참고 링크</h3>
<p><a href="https://velog.io/@jh100m1/CORS-%EC%97%90%EB%9F%AC%EA%B0%80-%EB%AD%94%EB%8D%B0-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%B4%EA%B2%B0%ED%95%98%EB%8A%94%EA%B1%B4%EB%8D%B0">CORS 가 뭔데 CORS 에러 어떻게 해결하는건데</a>
<a href="https://serendipity24.tistory.com/264">리액트에서 프록시 여러 개 설정하는 방법</a></p>
<p><em>→ 위의 방법은 로컬 개발환경에서는 제대로 동작하나, 배포했을때 api 응답이 html 페이지를 받아오는 문제가 있었다..</em></p>
<hr>
<h2 id="netlify-배포-시-api-호출-시-html-페이지로-응답되는-문제">Netlify 배포 시 api 호출 시 html 페이지로 응답되는 문제</h2>
<p>아무리 코드를 고쳐봐도 문제의 원인이 어디서 나온 것인지 정확히 파악하지 못해 의심가는 부분들을 테스트 해봤다. 심지어 400, 404, 500 같은 HTTP 에러가 뜬 것도 아니여서 멘붕이 왔다. 개발자 도구의 네트워크 탭에서 api 통신 요청과 응답을 하나하나 뜯어보니 아래의 1, 2번 문제는 아니었다. 정상적으로 request 되었고 env 변수도 알맞게 들어갔다. 응답이 왜 원하는 대로 되지 않는지…</p>
<ol>
<li><del>네이버 api url 잘못 설정</del></li>
<li><del>env 변수 잘못 입력</del></li>
<li>Proxy 에러</li>
<li>axios 통신 코드 문제</li>
</ol>
<p>3, 4에서 분명히 문제가 있을 것이라 판단했고, 맨 하단의 링크들을 참고하여 코드를 수정했다.</p>
<p> setupProxy.js 파일을 삭제하고 netlify.toml 파일을 새로 생성하여 proxy 설정을 따로 해줬다. </p>
<pre><code class="language-jsx">// netlify.toml
[[redirects]]
  from = &quot;/proxy/*&quot;
  to = &quot;https://openapi.naver.com/:splat&quot;
  status = 200
  force = true

  [[redirects]]
  from = &quot;/*&quot;
  to = &quot;/index.html&quot;
  status = 200
    force = false</code></pre>
<pre><code class="language-jsx">// Search.jsx
const PROXY = window.location.hostname === &#39;localhost&#39; ? &#39;&#39; : &#39;/proxy&#39;;
const URL = `${PROXY}/v1/search/book.json`;

const instance = axios.create({
      headers: {
         &#39;Content-Type&#39;: &#39;application/json&#39;,
         Accept: &#39;application/json&#39;,
         &#39;X-Naver-Client-Id&#39;: process.env.REACT_APP_NAVER_CLIENT_ID,
         &#39;X-Naver-Client-Secret&#39;: process.env.REACT_APP_NAVER_CLIENT_SECRET,
      },
   });

async function fetchData(query) {
      try {
         const response = await instance.get(URL, {
            params: {
               query: query,
               display: 60,
               ...</code></pre>
<p>인스턴스를 생성해준다. 여기서 공부가 부족하다고 느꼈던게, 처음에는 인스턴스를 생성하지 않고 get으로 받아오려고만 해서 제대로 api 요청이 되지 않았던 것 같다. 문제를 해결하느라 엄청난 시간을 쏟아버렸고 문제의 원인을 제대로 파악하지 못해 시간이 걸린 이유도 있었다. 네트워크 통신 공부해야겠다…😥</p>
<h3 id="참고-링크-1">참고 링크</h3>
<p><a href="https://www.youtube.com/watch?v=VaAWIAxvj0A&amp;t=1223s">Create React App - Proxy</a>
<a href="https://docs.netlify.com/routing/redirects/rewrites-proxies/#app">Netlify 공식 문서</a>
<a href="https://blog.jim-nielsen.com/2020/a-cors-proxy-with-netlify/">참고 블로그 링크1</a>
<a href="https://velog.io/@newjin46/TIL-React-Netlify-%EB%B0%B0%ED%8F%AC-%EC%8B%9C-Proxy-%EC%98%A4%EB%A5%98-Router-404-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0">참고 블로그 링크2</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[가비아 + netlify] 도메인 연결해서 검색 결과 나오게 하기]]></title>
            <link>https://velog.io/@easy_dev/%EA%B0%80%EB%B9%84%EC%95%84-netlify-%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%97%B0%EA%B2%B0%ED%95%B4%EC%84%9C-%EA%B2%80%EC%83%89-%EA%B2%B0%EA%B3%BC-%EB%82%98%EC%98%A4%EA%B2%8C-%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@easy_dev/%EA%B0%80%EB%B9%84%EC%95%84-netlify-%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%97%B0%EA%B2%B0%ED%95%B4%EC%84%9C-%EA%B2%80%EC%83%89-%EA%B2%B0%EA%B3%BC-%EB%82%98%EC%98%A4%EA%B2%8C-%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 20 Jul 2023 15:46:50 GMT</pubDate>
            <description><![CDATA[<p>이전에 지인의 포트폴리오로 개발했던 리액트 프로젝트가 하나 있어, 가비아에 도메인 연결을 해봤다. SEO 공부할 겸 구석에 썩혀두기에는 아까운 포폴이라 한번 해보고 싶었다. 지식은 얕지만 조금이나마 도움이 되는 글이 되었으면 하는 마음으로 기록을 남깁니다.</p>
<h1 id="도메인-연결하기">도메인 연결하기</h1>
<h2 id="netlify로-배포한-프로젝트-연결">netlify로 배포한 프로젝트 연결</h2>
<p>포폴 사이트는 리액트로 만든 프로젝트고 netlify로 배포했다. netlify 공식 사이트에 들어가서 로그인하면 배포한 프로젝트들을 확인해볼 수 있다. 도메인 연결할 프로젝트의 설정으로 들어가보면 배포가 완료된 프로젝트일 경우 현재 2단계라고 표시가 되어있을거다. 이제 여기에 도메인 주소를 입력해줘야 한다. </p>
<h2 id="도메인-주소-구매하기">도메인 주소 구매하기</h2>
<p>나는 <code>gabia</code>에서 도메인 주소를 구매했다. 등록하려는 주소가 이미 등록된 상태였기 때문에 .shop으로 끝나는 도메인 주소를 할인된 가격으로 550원에 구매했다. 도메인 등록 사이트를 찾아보니 <code>gabia, 카페24 등</code>을 보통 많이 사용하는 것 같다. 무료로 도메인을 등록할 수 있는 <code>Freenom</code>도 있는데 안정적이지 않다는 글이 있었다. 여러군데 비교해서 선택하면 된다.</p>
<h1 id="seo-점수-확인하기">SEO 점수 확인하기</h1>
<p>아래 사이트에서 주소를 입력하면 사이트를 분석해서 SEO 점수가 어떠한지 알려준다. 계정없이 무료로 사용할 경우 하루에 1개의 사이트만 확인 가능한 것 같다.
<a href="https://seositecheckup.com/">seositecheckup</a></p>
<h2 id="사이트맵-작성">사이트맵 작성</h2>
<p><img src="https://velog.velcdn.com/images/easy_dev/post/985d7eb0-e66f-4067-8353-afe0f7968244/image.png" alt="xml sitemap 생성">
sitemap.xml을 다운로드 받은 후 구글 서치 콘솔 &gt; sitemap 메뉴로 들어갑니다.
검색엔진 크롤러가 사이트맵을 찾을 수 있도록 검색엔진별로 제출하는 것이 필요하다.</p>
<p><img src="https://velog.velcdn.com/images/easy_dev/post/ee570bd9-a963-4382-a569-6381eca192b6/image.png" alt="">
위와 같이 했는데 구글 검색하면 검색 결과에 내 사이트가 나오긴 했다. 성공! 그런데 사이트 접속해보니 sitemap 그대로 출력되고 결과물은 보이지 않는다. 사이트맵이 제대로 설정되지 않은 것이라 생각되서 뒤이어 포스팅을 올리겠습니다.</p>
<p><a href="https://headwing.tistory.com/77">리액트 프로젝트 사이트맵 생성</a></p>
<pre><code>yarn add react-router-sitemap</code></pre><h3 id="-후기">+ 후기</h3>
<p>다른 프로젝트 하느라 며칠 뒤에 다시 구글 검색해보니 정상적으로 사이트가 보였다! 제대로 설정한 것인지는 모르겠으나 사이트맵 등록하고 나서 정상적으로 나오지 않을 수 있다는 글을 어디선가 읽은 것 같다. 며칠 지나야 한다는 것 같기도 하고... 그래도 어쨌든 뿌듯하다!</p>
<h4 id="도움-받은-사이트">도움 받은 사이트</h4>
<p><a href="https://seo.tbwakorea.com/blog/how-to-create-and-submit-a-sitemap/">사이트맵(sitemap.xml) 쉽게 만들고 제출하기!</a>
<a href="https://www.xml-sitemaps.com/">xml-sitemaps</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[검색엔진 최적화 방법]]></title>
            <link>https://velog.io/@easy_dev/%EA%B2%80%EC%83%89%EC%97%94%EC%A7%84-%EC%B5%9C%EC%A0%81%ED%99%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@easy_dev/%EA%B2%80%EC%83%89%EC%97%94%EC%A7%84-%EC%B5%9C%EC%A0%81%ED%99%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Mon, 17 Jul 2023 16:03:35 GMT</pubDate>
            <description><![CDATA[<p>들어가기에 앞서, Google 검색 센터 문서에 있는 &#39;검색엔진 최적화 기초&#39;를 읽고 작성하는 글입니다. SEO에 대한 공부를 목적으로 작성하는 글이므로 잘못된 정보는 피드백을 남겨주시면 수정하겠습니다.</p>
<p>SEO를 알아보기 전에, 검색이 어떤 방식으로 동작하는지 소개하는 글이 있어 이해를 돕기 위해 간단하게 적어봤다.</p>
<h2 id="구글-검색의-작동-방식">구글 검색의 작동 방식</h2>
<p>구글 검색은 자동화된 검색엔진인 웹 크롤러라는 소프트웨어를 사용하여 정기적으로 웹을 탐색한다. Google 색인에 추가할 페이지를 찾는데, 검색결과에 표시되는 페이지의 대부분 사이트 소유자가 검색결과를 위해 사이트를 직접 제출한 것이 아닌 Google 웹 크롤러가 웹 탐색시 발견한 사이트를 자동으로 추가한 것이다.</p>
<h3 id="구글-검색-3단계">구글 검색 3단계</h3>
<ol>
<li>크롤링 : 크롤러라는 자동화된 프로그램(Googlebot)을 사용해 인터넷에서 찾은 페이지로부터 텍스트, 이미지, 동영상을 다운로드<blockquote>
<p>발견한 페이지를 모두 크롤링하는 것은 아니다. 사이트 소유자가 크롤링을 허용하지 않는 페이지도 있고, 사이트에 로그인해야 액세스할 수 있는 페이지도 있다. 웹 사이트 중에는 자바스크립트를 사용해 페이지에 콘텐츠를 표시하는 경우가 많은데 크롤링하는 동안 Google은 페이지 렌더링시 발견된 자바스크립트를 실행하여 콘텐츠를 확인한다.</p>
</blockquote>
</li>
<li>색인 생성 : 페이지의 텍스트, 이미지, 동영상 파일을 분석하고 대규모 데이터베이스인 Google 색인에 이 정보를 저장<blockquote>
<p>페이지 내용을 파악하기 위한 단계를 색인 생성이라 하는데 <code>title</code> 요소 및 Alt 속성, 이미지, 동영상 등 텍스트 콘텐츠 및 핵심 콘텐츠 태그와 속성을 처리하고 이를 분석한다. 색인 생성은 페이지 콘텐츠 및 메타데이터에 따라  달라지는데, 페이지의 콘텐츠 품질이 낮거나 Robots <code>meta</code> 규칙이 색인 생성을 허용하지 않는다거나 웹사이트 디자인에 따라 색인 생성이 어려울 수 있다.</p>
</blockquote>
</li>
<li>검색결과 게재 : 사용자가 검색 시 사용자의 검색어와 관련된 정보를 반환<blockquote>
<p>Google 컴퓨터는 색인에서 일치하는 페이지를 검색해 품질이 가장 높고 사용자의 검색어와 가장 관련성이 크다고 판단되는 결과를 반환한다. 관련성은 사용자의 위치와 언어, 기기(데스크탑 or 핸드폰)와 같은 정보를 비롯한 수많은 요인으로 결정된다.</p>
</blockquote>
</li>
</ol>
<p><a href="https://developers.google.com/search/docs/fundamentals/how-search-works?hl=ko">Google 검색 작동 방식</a>
<a href="https://developers.google.com/search/docs/appearance/visual-elements-gallery?hl=ko">검색 UI 요소</a></p>
<h2 id="검색-엔진-최적화search-engine-optimization-seo✨">검색 엔진 최적화(Search Engine Optimization, SEO)✨</h2>
<blockquote>
<p>웹페이지 검색 엔진이 자료를 수집하고 순위를 매기는 방식에 맞게 웹페이지를 구성해서 검색 결과의 상위에 나올 수 있게 한다. 웹페이지와 관련된 검색어로 검색한 검색 결과 상위에 나오게 된다면 방문 트래픽이 늘어나기 때문에 효과적인 인터넷 마케팅 방법 중의 하나이며 비용처리 없는 마케팅이라고 할 수 있다.
출처 : 위키피디아</p>
</blockquote>
<p>사용자에게 도움이 되는 웹사이트를 빌드하고 사용자 환경을 개선하는 것을 목표로 최적화해야 한다.</p>
<h3 id="사용자에게-정확한-콘텐츠-제공하기">사용자에게 정확한 콘텐츠 제공하기</h3>
<h4 id="고유하고-정확한-페이지-제목">고유하고 정확한 페이지 제목</h4>
<p><code>title</code> 요소로 사이트의 각 페이지에 고유한 제목과 주제를 명확히 작성. <code>title</code> 요소의 텍스트가 너무 길거나 관련성이 낮으면 텍스트의 일부만 표시하거나 검색결과에 자동으로 생성된 제목 링크를 표시할 수 있다.</p>
<pre><code>&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Brandon&#39;s Baseball Cards - Buy Cards, Baseball News, Card Prices&lt;/title&gt;
    &lt;meta name=&quot;description&quot; content=&quot;Brandon&#39;s Baseball Cards provides a large selection of
    vintage and modern baseball cards for sale.
    We also offer daily baseball news and events.&quot;&gt;
&lt;/head&gt;
&lt;body&gt;
...</code></pre><h4 id="메타-설명-태그-사용">메타 설명 태그 사용</h4>
<p>페이지의 <code>meta</code> 설명 태그는 페이지 내용을 요약하여 제공한다. 이 메타 태그를 검색결과에서 페이지의 스니펫으로 사용할 수 있다. 사용자가 <code>site:</code> 검색 연산자를 사용해 검색했을 때 검색에 도움이 될 수 있다. <code>description</code> <code>meta</code> 태그 길이의 최솟값과 최댓값은 없다.</p>
<blockquote>
<p><strong>콘텐츠 설명 작성 시 권장 사항</strong></p>
</blockquote>
<p>1) 흥미롭고 정확하게 콘텐츠를 요약한 설명
2) 표제 태그(<code>h1 ~ h6</code>) 사용하여 중요한 텍스트 강조</p>
<p>자세한 문서는 <a href="https://developers.google.com/search/docs/appearance/snippet?hl=ko#meta-descriptions">우수한 메타 설명을 만드는 방법</a> 을 읽어보면 되겠다.</p>
<h3 id="사이트-계층-구조-구성하기">사이트 계층 구조 구성하기</h3>
<blockquote>
<p><strong>사이트 구성 권장 사항</strong></p>
</blockquote>
<p>1) url에 단어를 사용하면 사이트의 콘텐츠와 구조를 빠르게 파악할 수 있다.
2) 간단한 디렉토리 구조</p>
<p>단순한 URL을 사용해 웹사이트 문서와 관련된 설명을 제공하는 카테고리 및 파일 이름을 만들면 사이트를 더 잘 구성할 수 있고 콘텐츠를 보는 사용자가 쉽게 이해하여 읽을 수 있다. </p>
<pre><code>&lt;!-- 파악하기 어려운 url --&gt;
https://www.brandonsbaseballcards.com/folder1/22447478/x2/14032015.html

&lt;!-- 파악하기 쉬운 url --&gt;
https://www.brandonsbaseballcards.com/article/ten-rarest-baseball-cards.html</code></pre><h3 id="이미지-최적화하기">이미지 최적화하기</h3>
<h4 id="html-이미지-사용">HTML 이미지 사용</h4>
<ul>
<li>HTML <code>img</code> 또는 <code>picture</code> 요소를 사용한다. 시맨틱 HTML 마크업을 사용하면 크롤러가 이미지를 찾고 처리할 수 있다. <code>picture</code> 요소를 사용하여 반응형 이미지를 위한 다양한 화면 크기에 여러 옵션을 지정할 수도 있다. 이미지에 <code>landing="lazy"</code> 속성을 사용하면 사용자가 페이지를 더 빠르게 로드할 수도 있다. </li>
<li><code>alt</code> 속성 사용</li>
<li>JPEG, GIF, PNG, BMP, WebP 의 표준 이미지 형식 사용</li>
</ul>
<h3 id="모바일-친화적인-사이트-만들기">모바일 친화적인 사이트 만들기</h3>
<blockquote>
<ol>
<li>반응형 웹디자인</li>
<li>모바일 버전의 별도 URL</li>
</ol>
</blockquote>
<p><a href="https://developers.google.com/search/docs/fundamentals/get-on-google?hl=ko#is-your-website-secure">Google 검색엔진 최적화 기초</a></p>
<hr>
<h2 id="마치며">마치며</h2>
<p>구글 공식 문서를 참고한 것이라 SEO를 제대로 숙지했다고 보기는 어려울 것 같다. 그래도 기본적인 이해를 하는데 도움이 되었고 구글 검색을 사용하는 경우가 많기 때문에 유용한 정보를 얻을 수 있었다. 크롤링 개념과 크롤러가 어떻게 활용되는지 또한 알 수 있었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[class component에서 navigate 사용하기]]></title>
            <link>https://velog.io/@easy_dev/class-component%EC%97%90%EC%84%9C-navigate-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@easy_dev/class-component%EC%97%90%EC%84%9C-navigate-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 20 Apr 2023 07:26:26 GMT</pubDate>
            <description><![CDATA[<h1 id="클래스-컴포넌트">클래스 컴포넌트</h1>
<p>auth-service.js에는 Firebase Authentication을 처리하는 로직이 들어있다.</p>
<pre><code class="language-jsx">// auth-service.js
class AuthService {
   constructor(app) {
      this.firebaseAuth = getAuth(app);
   }

   logout() {
    // 로그아웃 
   }

   emailSignIn(email, password) {
        // 이메일로 회원가입
   }

   checkedEmailPassword(email, password) {
     // 로그인 시 가입한 정보와 일치하는지 확인 후 home으로 이동 
   }</code></pre>
<p>회원가입과 로그인, 로그아웃을 담당하는 class인데 여기서 문제가 발생했다. checkedEmailPassword() 함수에서 서버로부터 데이터를 받아온 뒤, 가입한 정보가 로그인할 때 입력한 정보와 같으면 페이지 이동을 해야했다.(같지 않으면 switch 문을 이용해 error.code에 따라 각각의 에러메시지를 띄우도록 했다.) </p>
<p>Authentication을 처리하는 로직을 class로 작성한 이유는 login, signin 컴포넌트 뿐만 아니라 profile, post 컴포넌트에서도 이 데이터가 필요하기 때문에 class를 사용한 것이고, function 컴포넌트로 작성하자니 코드 양이 많아질 것 같았다.. 최대한 class 컴포넌트를 사용하되, 페이지 이동도 가능하도록 구현하고 싶어서 class 컴포넌트에서 사용할 수 있는 navigation이 있을까 찾아봤다.</p>
<br/>

<p>참고한 링크 : 
<a href="https://dbstndi6316.tistory.com/366">https://dbstndi6316.tistory.com/366</a>
<a href="https://github.com/remix-run/react-router/issues/8146">https://github.com/remix-run/react-router/issues/8146</a>
<br/></p>
<h2 id="1-클래스-컴포넌트-파일-내에서-usenavigate-사용하는-방법">1. 클래스 컴포넌트 파일 내에서 useNavigate 사용하는 방법</h2>
<pre><code class="language-jsx">// in hocs.js
function withNavigation(Component) {
  return props =&gt; &lt;Component {...props} navigate={useNavigate()} /&gt;;
}

function withParams(Component) {
  return props =&gt; &lt;Component {...props} params={useParams()} /&gt;;
}

// in BlogPost.js
class BlogPost extends React.Component {
  render() {
    let { id } = this.props.params;
    // ...
  }
}

export default withParams(BlogPost);</code></pre>
<p>깃허브 질문에 달린 답변 중에 위 코드로 시도해봤으나 내 코드(아래 코드)는 index.js에서 firabaseApp을 넘겨 받아야하기 때문에 에러가 발생했다.. 
<br/></p>
<h2 id="2-navigate를-인자로-전달하는-방법">2. navigate를 인자로 전달하는 방법</h2>
<pre><code class="language-jsx">// index.js
const authService = new AuthService(firebaseApp);

const root = ReactDOM.createRoot(document.getElementById(&#39;root&#39;));
root.render(
   &lt;ThemeProvider theme={theme}&gt;
      &lt;App authService={authService} /&gt;
   &lt;/ThemeProvider&gt;,
);</code></pre>
<p>다른 방법이 없을까 열심히 찾아봤는데 class 컴포넌트에서 navigate를 사용하는 방법보다는 function 컴포넌트에서 사용하는 걸 권장한다는 글이 있었던 것 같다. 비록 깊지 않은 지식을 갖고 있지만 꼭 해결하고 싶어서 내 코드를 열심히 뜯어봤다. 깃허브에 달린 답변을 힌트 삼았다. 
<br/></p>
<pre><code class="language-jsx">// Login.jsx
const navigator = useNavigate();
authService.checkedEmailPassword(navigator, userEmail, userPassword);</code></pre>
<p>navigator 변수에 useNavigate() 함수를 할당해 checkedEmailPassword() 함수의 인자로 전달하는 방법을 시도했다. 확인 결과, 문제없이 잘 동작했다. 더 나은 방법이 있을 수도 있겠지만 현재로서는 최선의 방법으로 해결했다!</p>
<h3 id="구현-화면">구현 화면</h3>
<img src="https://velog.velcdn.com/images/easy_dev/post/e3740eb0-71b6-426b-b018-fba631039511/image.gif" width="300" height="300"/>

]]></description>
        </item>
    </channel>
</rss>