<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sopt_official.log</title>
        <link>https://velog.io/</link>
        <description>IT 대학생벤처창업동아리 SOPT의 공식 블로그입니다.</description>
        <lastBuildDate>Tue, 24 Jan 2023 10:33:36 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>sopt_official.log</title>
            <url>https://velog.velcdn.com/images/sopt_official/profile/25f084aa-9171-4a05-ae02-d50d0eb1d3d4/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. sopt_official.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sopt_official" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[9개월 묵은 APPJAM 코드 뜯어내기 :: Recoil Selector Async Fetching]]></title>
            <link>https://velog.io/@sopt_official/9%EA%B0%9C%EC%9B%94-%EB%AC%B5%EC%9D%80-APPJAM-%EC%BD%94%EB%93%9C-%EB%9C%AF%EC%96%B4%EB%82%B4%EA%B8%B0-Recoil-Selector-Async-Fetching</link>
            <guid>https://velog.io/@sopt_official/9%EA%B0%9C%EC%9B%94-%EB%AC%B5%EC%9D%80-APPJAM-%EC%BD%94%EB%93%9C-%EB%9C%AF%EC%96%B4%EB%82%B4%EA%B8%B0-Recoil-Selector-Async-Fetching</guid>
            <pubDate>Tue, 24 Jan 2023 10:33:36 GMT</pubDate>
            <description><![CDATA[<h4 id="🔗-이전-게시글-tistory"><strong><a href="https://snupi.tistory.com/213">🔗 이전 게시글 (Tistory)</a></strong></h4>
<p>(위 링크의 글을 보시면, 흐름 이해가 쉽습니다)</p>
<p>하나의 atom 상태를 가지고 컴포넌트마다의 커스텀 훅을 불러오려니, 중복적인 서버 통신이 일어난다.</p>
<p>Recoil 로써 캐싱을 할 방법은 없을지 알아보자.</p>
<h2 id="recoil-selector--loadable">Recoil Selector + Loadable</h2>
<h4 id="1-효율적인-서버-통신">1. 효율적인 서버 통신</h4>
<p>각 reviewId 에 맞는 책 정보 데이터를 비동기로 불러와 관리하고,</p>
<p>각 컴포넌트에서의 중복적인 데이터 패칭을 막기 위해 selectorFamily를 이용하였다.</p>
<p>이 때 selector는 캐싱 기능을 제공하며, 한번의 api call으로 처리할 수 있도록 도와준다.</p>
<p>Recoil은 <a href="https://recoiljs.org/docs/guides/asynchronous-data-queries/#asynchronous-example">📎 selector로 비동기 패칭</a>을 지원한다.</p>
<p>구현 방법은 간단하니, 문서를 참고하면 좋을 것 같다.</p>
<p>Recoil 를 이용하여 비동기로 데이터를 패칭하는 여러 방법이 있다.</p>
<p><img src="https://velog.velcdn.com/images/sopt_official/post/c1dd6b62-dae5-4b63-aa97-0b9eadab877b/image.png" alt=""></p>
<p>“fetch only one at the time, using a selectorFamily”</p>
<p>앞 포스팅에서 보였던 꼬리 질문/답변 기능은 “비동기로 패칭한 데이터를 기반한 전역 state”가 필요했다.</p>
<p>selectorFamily로 비동기 패칭을 구현하고, 이를 기반한 전역 state는 atom 으로써 동작하게끔 한다.</p>
<p><img src="https://velog.velcdn.com/images/sopt_official/post/4582ec8b-1f3d-40ce-99c6-8b79051c9631/image.png" alt="Recoil Selector"></p>
<p>Selector 로 비동기 패칭이 완료된 데이터는 Atom 에 업데이트 한다.</p>
<p>이와 같은 구조로 설계함으로써 아래와 같이 수많은 중복 통신을 방지할 수 있었다.</p>
<p><img src="https://velog.velcdn.com/images/sopt_official/post/6ed0d2d6-a2fc-4534-b0b2-83b3ebb3a5ab/image.png" alt="Before (중복 통신)"></p>
<p>-&gt;</p>
<p><img src="https://velog.velcdn.com/images/sopt_official/post/e0fe294f-2b40-4caa-ad37-312b74037f03/image.png" alt="After (Selector Async Fetching)"></p>
<h4 id="2-suspense-처리">2. Suspense 처리?</h4>
<p>⛔️</p>
<p>Recoil 은 Facebook에서 만들어진 라이브러리로, Suspense와의 호환성이 좋다.</p>
<p>하지만 작업을 하다보니</p>
<p>Next app 을 build 할 때, static page 를 생성할 때에 계속해서 timeout이 발생해 build가 되지 않는 이슈가 발생하기도 한다 (static-page-generation-timeout)</p>
<p>찾아보니, Next 에서 Recoil의 selector 와 Suspense 를 함께 사용할 수 없는 <a href="https://github.com/vercel/next.js/issues/37372">📎 이슈</a>가 있었다.</p>
<p>◽
◽
◽</p>
<p>왜 그럴까?</p>
<p>◽</p>
<p>RQ, SWR과 같은 패칭 라이브러리는 fetch-on-render 의 방식으로,</p>
<p>컴포넌트 렌더링 이후에 네트워크 요청을 한다.</p>
<p>◽</p>
<p>React18의 Suspense 기능은 render as you fetch 을 지원하는데,</p>
<p>네트워크 요청을 하고 컴포넌트 렌더링을 이뤄지도록 한다. 이 때, 데이터가 다 불러와짐(stale data)을 가정하고 fallback으로 넘겨준다.</p>
<p>에러 대신 Promise를 throw 하는 것을 Suspense가 감지할 수 있게 된다.</p>
<p>◽</p>
<p>이를 이용해 Recoil async selector는 Promise를 throw 하여 Suspense를 지원하도록 하지만,</p>
<p>Next app의 SSR에서는 데이터 패칭이 끝나지 않아 timeout 이 일어나는 게 아닐까 하는 <a href="https://github.com/Tekiter">📎 의견</a>이 있다.</p>
<p>◽
◽
◽</p>
<p>이를 해결하기 위해 Recoil은 fetch-on-render 방식의 Loadable을 지원한다.</p>
<p>(아쉽지만 로딩처리에 대한 로직 구현을 Suspense 로의 추상화는 다음 과제로 남겨놓고,)</p>
<p><a href="https://recoiljs.org/ko/docs/api-reference/core/useRecoilValueLoadable/">📎 useRecoilValueLoadable</a> 훅으로 세가지 상태에 대한 예외 처리를 해주자. (이 역시 공식문서의 예제가 잘 나와있기에, 설명을 대신 해주리라 생각해요)</p>
<p>useRecoilValueLoadable 훅은 Suspense를 위해 비동기로 값을 읽어올 때 Error 혹은 Promise를 던지지 않고, “Loadable” 객체를 리턴한다.</p>
<p>hasValue, loading, hasError 3가지 상태의 Loadable 객체로써 컴포넌트를 분기 처리하여 나타낸다.</p>
<p><img src="https://velog.velcdn.com/images/sopt_official/post/5f30af20-4558-4b6d-a872-dcb27a4ee735/image.png" alt="Next app Build"></p>
<p><a href="https://github.com/TeamBookTez/nextjs-book-stairs/pull/63">📎 Build 까지 완료 🔐</a></p>
<h2 id="글을-줄이며">글을 줄이며</h2>
<p>하여,</p>
<p>Recoil 을 활용해 <strong>atomic 한 전역적인 상태를 관리</strong>할 수 있었고,</p>
<p>해당 페이지에 대해 <strong>중복되는 서버통신을 캐싱하여 막아주었다</strong></p>
<p>◽</p>
<p>설계 양, 사용법이 다소 간단한 Recoil 을 사용하여 작업 시간이 그렇게 길지는 않았지만, 사용할 수록 아쉬운 부분들도 많았다</p>
<p>특히, 서버 사이드 데이터 관련한 기능이 react-query, swr에 비교도 안 될만큼 부족하고,</p>
<p>데이터 갱신과 같이 불안정한 기능들이 많다.. <a href="https://github.com/facebookexperimental/Recoil/issues">📎 수많은 ISSUE들</a>이 대기 중이다</p>
<p>그러나 간단하게 전역으로 상태를 관리하고, 로직을 효율적으로 설계하여 관리하기에는 굉장히 편리하였기에, 서비스에 맞게끔 맞는 기술을 사용하면 좋겠다.</p>
<p>◽</p>
<p>북스테어즈는 이 작업을 시발점으로 다시금 작업을 시작하게 되었다.</p>
<p>개발을 처음 접하던 우리는 기본기에 충실하며 라이브러리를 최소화하는 개발을 하였다.</p>
<p>심지어 객체의 깊은 복사, Debounce, Unmount Animation 모두 직접 구현하였다.</p>
<p>그리고 지금, 로딩 및 에러처리나, 반응형 작업이나, 부분부분의 리팩토링, … 뜯어낼 구석이 많지만 :( …~</p>
<p>오히려 이 과정이 개념을 더 깊게 와닿게 해주었고 더 멀리 나아갈 수 있다는 자신감을 주었다.</p>
<p>이 모두가 팀원들에게 성장의 발판이 되기를 바란다.</p>
<hr>
<h4 id="🔗-해당-게시글-보러가기-tistory"><strong><a href="https://snupi.tistory.com/214">🔗 해당 게시글 보러가기 (Tistory)</a></strong></h4>
<p><strong>작성자</strong> :: <a href="%5Bhttps://github.com/choichoijin%5D(https://github.com/choichoijin)">IN SOPT 웹파트장 이주함</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프론트엔드 협업은 어떻게 할까?]]></title>
            <link>https://velog.io/@sopt_official/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%ED%98%91%EC%97%85%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%A0%EA%B9%8C</link>
            <guid>https://velog.io/@sopt_official/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%ED%98%91%EC%97%85%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%A0%EA%B9%8C</guid>
            <pubDate>Tue, 24 Jan 2023 10:18:21 GMT</pubDate>
            <description><![CDATA[<h1 id="👨🏻💻프론트엔드-협업은-어떻게-할까👩🏻💻">👨🏻‍💻프론트엔드 협업은 어떻게 할까?👩🏻‍💻</h1>
<h2 id="🗓️기능분석개발-일정-및-리소스-배분">🗓️기능분석,개발 일정 및 리소스 배분</h2>
<h3 id="📝구현-기능-분석">📝구현 기능 분석</h3>
<p>요리를 개발할 때 재료가 얼만큼 들어가고 조리를 어떻게 해아하는지 레시피를 꼼꼼하게 적는것 처럼,<br>
개발을 할 때에도 구현 기능을 꼼꼼하게 살펴보고 무엇을 개발해야하는지 문서화 해놓는것이 굉장히 중요하다.<br>
기획자 및 팀원들과 충분한 상의를 통하여 어떤 기능을 구현해야하는지 서로가 &#39;명확하게&#39; 알고 있는것이 중요하다!!</p>
<p>예시:<br>
🙋🏻‍♂️ 프론트 엔드 : 적용했으면 하는 차트 라이브러리가 있을까요?!<br>
🙋🏻‍♀️ 기획자 : 기존에 OOO사이트에서 사용했던 라이브러리를 사용해주세요!<br>
<br>
🙋🏻‍♀️ 디자이너 : 차트 라이브러리 사용하면 커스텀 할 수 있는 부분이 어떤게 있을까요?!<br>
🙋🏻‍♂️ 프론트 엔드 : 색상, 텍스트 크기, 라벨 등등 가능합니다!<br>
<br>
🧑🏼‍💻 백엔드 : 날짜데이터 전달해 드릴 때 문자열 &#39;2022-12-01&#39; 이런식으로 드리면 되나요?!<br>
🙋🏻‍♂️ 프론트 엔드 : 아뇨! 년,월,일 로 나누어서 객체형식으로 나누어서 숫자형식으로 보내주세요!<br></p>
<p>이렇게 대화를 통해 결정된 부분들을 문서화를 통해 정해놓는다면 혼란스러움을 방지할 수 있다!<br>
서로의 입장을 이해해 주면서 최대한 정중하고 이해하기 쉬운 용어를 이용해서 대화하는것을 지향한다!</p>
<p>이렇게 상의를 통해 얻은 결론들을 프로젝트 가장 상위 디렉토리에 REQUIREMENTS.md를 통해 문서화하거나,<br>
팀원들과 상의 하여 문서화하길 바란다!</p>
<p><img src="https://user-images.githubusercontent.com/70846061/205590523-e0e62c1b-8136-468a-9ab2-23ecba688335.png" alt="image"></p>
<h3 id="📆개발일정">📆개발일정</h3>
<p>위에서 작성한 구현 기능 분석한 문서를 통해 각 기능별로 소요 기간을 분석한다. 기간은 3가지로 나뉜다!</p>
<ul>
<li>Best : 기능 구현만 하고 어떠한 돌발상황이 발생하지 않았을 때 예측되는 소요시간</li>
<li>Normal : 일상적으로 예상할만한 변수를 반영한 소요시간 (디버깅, pr리뷰 피드백 반영,기획자 피드백)</li>
<li>Worst : 돌방상황들이 겹쳐 최악의 상황에서 걸리는 소요시간(팀원의 중도포기, 등등)</li>
</ul>
<p>이 중 Normal과 Worst 사이로 개발 기한을 설정한다.<br>
핵심기능 단위로 중간 점검일을 설정해, 일정대로 개발이 이뤄지고 있는지 확인하며 개발을 진행한다.<br>
<br></p>
<h3 id="📚리소스배분">📚리소스배분</h3>
<p>리소스 배분을 하면서도 팀원의 실력, 개발자의 인원 수, 기능의 수준에 따라서 고려해야할 사항이 많다.
배분 기준에는 다양한 요건이 있지만 <b>&#39;구현 기능&#39;</b>을 중심으로 진행한다!
예시를 알아보자!</p>
<ul>
<li>진행 순서에서 우선순위가 높은 기능을 먼저 나눈다.</li>
<li>기능은 가능하면 독립적으로 구현할 수 있도록 나눈다.</li>
<li>어려운 기능이 있거나, 처음 접해보는 기능이라면 페어코딩을 한다.</li>
</ul>
<br>

<h2 id="🖥️-작업환경-설정">🖥️ 작업환경 설정</h2>
<p>자 이제 본격적으로 개발을 시작하자!!??<br>
....<br>
잠깐!!!<br>
아직 설정해야할게 남아있다!!<br></p>
<h3 id="npm--yarn">npm ?? yarn??</h3>
<p><img src="https://user-images.githubusercontent.com/70846061/205592146-d52dc715-5325-4509-a536-2efe32b2255c.png" alt="npm_yarn">
npm, yarn 모두 노드(Node.js)의 패키지 관리자.
npm과 yarn을 통해 다양한 패키지를 설치 및 삭제가 가능하다.</p>
<p>왜 패키지 관리자가 npm yarn 두개나 있고 어떤걸 선택해서 개발을 진행해야할까?</p>
<p>npm과 yarn을 비교하는 대표적인 2가지를 알아보자.</p>
<ol>
<li><p>속도<br>
npm은 패키지를 설치할때 한번에 하나씩 순차적으로 설치하는 반면 yarn은 여러 패키지를 동시에 가져오고 설치하도록 최적화 되어있어 패키지 설치 속도 측면에서 우월하다!</p>
<ul>
<li>npm - 3.572 seconds</li>
<li>yarn - 1.44 seconds<br>
하지만 npm V6.10.1과 yarn V1.17.3으로 install 속도 실험을 하였는데,<br>
yarn이 승리하였지만 그 차이는 아주 근소한 차이라고 하여서, 이제는 거의 차이가 없어졌다고 볼 수 있는 문제인거 같다!</li>
</ul>
</li>
<li><p>보안<br>
yarn은 보안 측면에서 npm보다 더 안전한 것으로 알려져 있다. npm은 자동으로 패키지에 포함된 다른 패키지 코드실행을 허용한다. 이로 인해 보안 시스템에 몇 가지 취약성이 발생할 수 있다. 반면에 yarn은 yarn.lock or package.json에 있는 파일만을 설치한다.</p>
</li>
</ol>
<br>
npm이 yarn보다 아직까지는 점유율이 높고, yarn은 사람들이 관심을 더 가지고 있는 패키지 매니저 로서 팀원들의 취향을 이야기 하면서 개발을 진행하면 될거같다.

<h3 id="prettier--eslint">Prettier &amp; ESlint</h3>
<p>남이 해놓은 초기환경에 숟가락만 얹으려하니 이번 합동세미나 협업에서 식은땀이 줄줄 났던적이 있다.<br>
따라서 Prettier와 ESlint 팀원과 설정할 때 옵션을 어느정도 아는것도 중요하다고 생각이 든다.<br></p>
<h3 id="prettier">Prettier</h3>
<p><img src="https://user-images.githubusercontent.com/70846061/205592251-023ea5de-cf55-4f02-b8fb-c9e42d032e79.png" alt="1(25)"></p>
<p>정해진 규칙에 따라 자동으로 코드 스타일을 정리 해주는 도구이다.</p>
<p>각자의 코딩스타일 취향이 다르기 때문에 협업을 할 때에는 명확한 규칙이 존재해야한다!<br>
코드 스타일을 옵션을 통해 지정하여 팀원들이 보다 일관성 있는 코드를 작성할 수 있다!</p>
<p>설치 :<br>
yarn add -D prettier</p>
<p>.prettierrc.js의 속성 중 자주 사용하는 속성을 알아보자</p>
<pre><code>{
  semi: true,
  printWidth: 120,
  endOfLine: &#39;auto&#39;,
  singleQuote: true,
  useTabs: false,
  tabWidth: 2,
  trailingComma: &#39;all&#39;,
  arrowParens: &#39;always&#39;
};</code></pre><ul>
<li>semi: true - statement 마지막에 세미콜론을 찍음</li>
<li>printWidth: 120 - 선호되는 한 줄의 길이</li>
<li>endOfLine: &#39;auto&#39; - 파일의 마지막에는 EOL을 보장</li>
<li>singleQuote: true - 쌍따옴표가 아닌 홑따옴표를 사용</li>
<li>useTabs: false - 탭을 사용하지 않고 스페이스를 사용</li>
<li>tabWidth: 2 - 탭을 할 경우 2 스페이스</li>
<li>trailingComma: &#39;all&#39; - 여러줄로 나뉘었을 때는 쉼표를 사용</li>
<li>arrowParens: &#39;always&#39; - 화살표 함수에서 괄호 사용 의무화</li>
</ul>
<p>더 많은 설정은 <a href="https://prettier.io/docs/en/options.html">공식문서</a>를 참고하길 바란다.</p>
<h3 id="eslint">ESlint</h3>
<p><img src="https://user-images.githubusercontent.com/70846061/205592369-cc4276b8-1630-40b5-aa1c-57a9c67de0da.jpeg" alt="다운로드"></p>
<p>정해진 규칙에 따라 코드 퀄리티를 보장하도록 도와준다.<br>
<del>이게 머선 말이고?? prettier랑 똑같은거 아이가!</del></p>
<p>기능을 구현할 때에는 굉장히 많은 방식이 존재한다.<br>
함수를 구현할 때에도 function키워드를 사용하는 방법과 arrow function을 사용해서 구현하는 방법이 있다.</p>
<p>객체의 반복문을 사용할 때에는 for문을 사용할 수 있고, 내장 함수를 사용할 수 있고 등등 다양한 방법이 존재한다.<br>
이렇게 협업을 하게 되면 우리가 협업에서 추구하는 &#39;한사람이 코딩을 한것처럼 코딩하자&#39;의 목적에 점점 멀어지게 된다.<br>
<br>
따라서 <b>일관성 있는 방식</b>으로 구현할 수 있도록 잡아주는것이 eslint가 하는 역할이다!</p>
<p>설치 :<br>
yarn add -D eslint + 익스텐션 설치(필수)
<img src="https://user-images.githubusercontent.com/70846061/205594731-7b869bd3-b422-4f93-9808-a1cee7d5fe05.png" alt="스크린샷 2022-12-05 오후 5 52 51">
<br><br>
eslint 공식문서에 나와있는 예시를 보자!</p>
<pre><code>{
    &quot;root&quot;: true,
    &quot;plugins&quot;: [
        &quot;@typescript-eslint&quot;
    ],
    &quot;extends&quot;: [
        &quot;eslint:recommended&quot;,
        &quot;plugin:@typescript-eslint/recommended&quot;
    ],
    &quot;parser&quot;: &quot;@typescript-eslint/parser&quot;,
    &quot;rules&quot;: {
        &quot;@typescript-eslint/strict-boolean-expressions&quot;: [
            2,
            {
                &quot;allowString&quot; : false,
                &quot;allowNumber&quot; : false
            }
        ]
    }
}</code></pre><ul>
<li><p>root<br>
default : true<br>
false의 경우 해당 프로젝트 디렉토리 뿐 아니라 해당 PC의 root 디렉토리까지 eslint를 찾는다.</p>
</li>
<li><p>extends<br>
eslint rule 설정이 저장되어 있는 외부 file을 extends 하는 부분이다.<br>
위의 예시처럼 extends에 eslint:recommended,plugin:@typescipt-eslint/recommended를 장착하면, 사용하려는 해당 plugin에서 기본적으로 제공하는 rule set이 적용된다.</p>
</li>
<li><p>plugins</p>
<ul>
<li>eslint-config-airbnb-base: 에어비엔비 린트 플러그인</li>
<li>eslint-config-next: Next.js 전용 린트 플러그인</li>
<li>eslint-plugin-react: 리액트 전용 플러그인</li>
<li>eslint-plugin-prettier: 린트 위에 사용할 프리티어 플러그인</li>
<li>eslint-config-prettier: 요건 린트 설정과 중복되는 부분이 있으면 프리티어 룰에서 제외하는 플러그인</li>
<li>@typescript-eslint/eslint-plugin: : 타입스크립트 전용 린트
등등 다양한 종류가 존재한다.</li>
</ul>
</li>
<li><p>rules<br>
직접 lint rule들을 커스터마이징 하는 부분이다.<br>
extends로 자동으로 설정된 rules 중, 특정 rule을 끌 수 있다.<br>
어떤 설정을 어떻게 해야하는지는 공식문서에 상세하게 나와있기때문에 팀원과 상의하여 정하면 된다!<br></p>
</li>
<li><p>parser<br>
각 코드 파일을 검사할 parser를 설정하는 부분이다.<br>
특정 @typescript-eslint/eslint-plugin 처럼 특정 플러그인을 사용한다면 해당 플러그인에서 제공하는 parser를 장착하면 된다.</p>
</li>
</ul>
<h2 id="🐈⬛git-협업">🐈‍⬛git 협업</h2>
<p>은 글이 너무 길어지기 때문에 <a href="https://dhgu-dev.medium.com/%EB%A7%A8%EB%95%85%EC%97%90%EC%84%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-%ED%98%91%EC%97%85%EC%9D%84-%EC%9C%84%ED%95%9C-github-%EC%82%AC%EC%9A%A9%EB%B2%95-46f64418cf81">해당 아티클</a>을 참조하길 바란다!!</p>
<p>그럼 협업에서도 멋진 모습을 보여주길!! Good Luck!🍀</p>
<hr>
<p><strong>작성자</strong>
<a href="https://github.com/myeongheonhong">IN SOPT WEB, YB 홍명헌</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Chakra UI를 아시나요?]]></title>
            <link>https://velog.io/@sopt_official/Chakra-UI%EB%A5%BC-%EC%95%84%EC%8B%9C%EB%82%98%EC%9A%94</link>
            <guid>https://velog.io/@sopt_official/Chakra-UI%EB%A5%BC-%EC%95%84%EC%8B%9C%EB%82%98%EC%9A%94</guid>
            <pubDate>Tue, 24 Jan 2023 10:15:23 GMT</pubDate>
            <description><![CDATA[<h1 id="chakra-ui에-대해서">Chakra UI에 대해서</h1>
<img width="176" alt="스크린샷 2022-11-04 오후 11 02 48" src="https://user-images.githubusercontent.com/99077953/199991263-dfaacfcf-69d6-4961-92eb-8e7cc5812109.png">

<img width="139" alt="스크린샷 2022-11-04 오후 11 27 18" src="https://user-images.githubusercontent.com/99077953/199999395-46db9c15-2f3f-49b1-a61e-b68cf5322829.png">

<h2 id="1-chakra-ui-란">1. Chakra UI 란?</h2>
<p>“Chakra UI는 React 애플리케이션을 빌드하는데 필요한 블록을 제공하는 간단하고, 모듈적이며, 접근 가능한 컴포넌트 <strong>라이브러리</strong>입니다.”</p>
<ul>
<li><p><strong>접근성</strong> 표준 WAI-ARIA standards를 엄격하게 따른 개발</p>
</li>
<li><p><strong>다크모드 적용</strong>이 쉬움</p>
</li>
<li><p>prop-based</p>
</li>
<li><p>다양한 컴포넌트</p>
</li>
</ul>
<p>(Accordion, Alert, AlertDialog, AspectRatioBox, Avatar, Badge, Box, Breadcrumb, Button,Checkbox, CircularProgress, CloseButton, Code, Collapse, ControlBox, Divider, Drawer, Editable, Flex, FormControl, Grid, Heading, Icon, IconButton, Image, Input, Link, List, Menu, Modal, NumberInput, Popover, Progress, PseudoBox, Radio, SimpleGrid, Select, Skeleton, Slider, Spinner, Stat, Stack, Switch, Tabs, Tag, Text, Textarea, Toast, Tooltip 등등……)</p>
<ul>
<li>유틸리티</li>
</ul>
<p>(useClipboard : 클립보드로 내용을 복사하는 커스텀 훅,
useDisclosure : Modal, Drawer 등의 Open, Close, Toggle 을 지원하는 커스텀 훅,
useColorMode : 초기 설정 후 매우 간단하게 colorMode를 변경하게 해주는 훅 등등……..)</p>
<p>개발 스택을 정하던 중 차크라ui 말을 꺼낸 팀원의 말을 뒤로 하고 styled-components로 작업을 시작했습니다. 스프린트 형식으로 새로운 라이브러리를 배울 시간이 없을 것이라고 생각했기 때문이죠. 그러다 결국 다음 서비스 개발에서는 차크라ui를 써보기로 합니다. </p>
<p>🐣 : 대충 얘는 어떻게 쓰는 앤데?</p>
<p>🐻 : 얘는.. 밑에다가 스타일을 따로 지정해줄 필요 없어. <strong>props로 다 넘겨주면 돼</strong>!</p>
<img width="845" alt="스크린샷 2022-11-04 오후 11 03 13" src="https://user-images.githubusercontent.com/99077953/199991448-bb493202-60a2-4f42-a99d-a519952d4bfe.png">

<p>🐣 : ..?</p>
<p>처음 코드를 봤을 때는 이해가 가지 않았습니다. Box 컴포넌트..? Flex..? 따로 선언해준 컴포넌트가 없지만 위에서 import 하여 어떤 컴포넌트를 가져다 사용하고 있었습니다. 차크라ui 공식 홈페이지에 들어가 좀 더 살펴보기로 합니다. </p>
<p>&quot;Box is the most abstract component on top of which all other Chakra UI components are built. By default, it renders a <code>div</code> element&quot; 
아하 기본적으로 div라는 엘리먼트를 가져오는군요. 스타일 지정은 props로 해주나 봅니다.</p>
<img width="521" alt="스크린샷 2022-11-04 오후 11 03 34" src="https://user-images.githubusercontent.com/99077953/199991618-a9fe5025-302a-4e2d-9ef7-8ab8e3ce003f.png">

<img width="514" alt="스크린샷 2022-11-04 오후 11 03 47" src="https://user-images.githubusercontent.com/99077953/199991802-bdb97984-305a-4d8f-9102-8c5befee33bf.png">

<p>차크라ui 공식 홈페이지에서 처음 봤던 코드가 이해가 가기 시작합니다.
첫 Box는 오른쪽 구현사항의 가장 큰 box일 거고,, Image태그에 borderRadius라는 이름으로 md(medium)이라는 속성이 지정되어 있고.. 오 보기 편한데?</p>
<img width="845" alt="스크린샷 2022-11-04 오후 11 03 13" src="https://user-images.githubusercontent.com/99077953/199991448-bb493202-60a2-4f42-a99d-a519952d4bfe.png">

<p>확실히 스타일 코드 구조를 한 눈에 파악하기 쉬웠습니다. 이런 식으로 확인할 수 있다면 <code>position: absolute</code> 속성을 사용할 때도 어느 요소를 기준으로 잡고 있는지 확인하기도 편하겠다는 생각을 했습니다. </p>
<br />

<h2 id="2-chakra-ui-설치방법">2. Chakra UI 설치방법</h2>
<p>사용하는 방법은 아주 간단합니다!</p>
<p>1 - Chakra UI 설치</p>
<pre><code class="language-jsx">yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion</code></pre>
<p>2 - import, app의 root에서 ChackraProvider로 감싸주기</p>
<pre><code class="language-jsx">import { ChakraProvider } from &quot;@chakra-ui/react&quot;
function App() {
  return (
    &lt;ChakraProvider&gt;
      &lt;App /&gt;
    &lt;/ChakraProvider&gt;
  )
}</code></pre>
<br />

<h2 id="3-usecolormode">3. useColorMode()</h2>
<p><strong>dark mode, light mode</strong></p>
<p>차크라ui의 편리한 점 중 하나는 컬러 모드를 변경하는 useColorMode라는 커스텀 훅(hook)을 제공합니다. 차크라ui는 localStorage에 color mode를 저장합니다. </p>
<p>사이트 공식 문서에 정리가 잘 되어 있는데요. js 코드와 더불어 typescript 유저들을 위한 ts용 코드까지 나와있습니다. 문서 내용을 따라 해당 파일을 추가해줍니다. </p>
<p><strong>theme.ts</strong></p>
<pre><code class="language-tsx">// theme.ts

// 1. import `extendTheme` function
import { extendTheme, type ThemeConfig } from &#39;@chakra-ui/react&#39;

// 2. Add your color mode config
const config: ThemeConfig = {
  initialColorMode: &#39;light&#39;,
  useSystemColorMode: false,
}

// 3. extend the theme
const theme = extendTheme({ config })
export default theme</code></pre>
<p>계속 읽다보면 next.js를 사용한다면 추가해야하는 파일을 알려 줍니다. _document.js파일 안에 ColorModeScript를 추가하라고 하네요.</p>
<p><strong>pages/_document.js</strong></p>
<pre><code class="language-tsx">import { ColorModeScript } from &#39;@chakra-ui/react&#39;
import NextDocument, { Html, Head, Main, NextScript } from &#39;next/document&#39;
import theme from &#39;./theme&#39;
export default class Document extends NextDocument {
  render() {
    return (
      &lt;Htmllang=&#39;en&#39;&gt;
        &lt;Head /&gt;
        &lt;body&gt;
          {/* 👇 Here&#39;s the script */}
          &lt;ColorModeScriptinitialColorMode={theme.config.initialColorMode} /&gt;
          &lt;Main /&gt;
          &lt;NextScript /&gt;
        &lt;/body&gt;
      &lt;/Html&gt;
    )
  }
}</code></pre>
<p>아래에 있는 ColorModeSwitcher.tsx는 light mode와 dark mode를 토글 시켜주는 버튼 컴포넌트입니다.</p>
<p><strong>ColorModeSwitcher.tsx</strong>
<img width="488" alt="스크린샷 2022-11-04 오후 11 04 22" src="https://user-images.githubusercontent.com/99077953/199992178-be772715-7637-449d-8c16-e456661b717d.png"></p>
<p>버튼을 클릭하면 라이트 모드 ↔ 다크 모드간 토글이 가능합니다!</p>
<p>추가로, <code>useColorModeValue</code>를 통해 각 모드에 원하는 값을 커스텀할 수 있습니다.</p>
<br />

<h2 id="4-그-외-유용한-기능-컴포넌트-중심">4. 그 외 유용한 기능 (컴포넌트 중심)</h2>
<h3 id="4-1-flex-컴포넌트">4-1. Flex 컴포넌트</h3>
<p>팀원한테 차크라ui 영업을 당하면서 가장 솔깃했던 기능 <code>Flex 태그</code> 입니다. 무지하게 자주 쓰는 <code>div 태그</code>에 <code>display: flex</code>를 프로젝트하면서 한 x100000번 정도 쓴 것 같은데요. 차크라에서는 Flex라는 태그로 대신할 수 있습니다.  </p>
<img width="584" alt="스크린샷 2022-11-04 오후 11 04 49" src="https://user-images.githubusercontent.com/99077953/199992507-d9dba827-a5f5-497d-bee5-9ba75a1f9497.png">

<h3 id="4-2-center-컴포넌트">4-2. Center 컴포넌트</h3>
<p><code>flex div</code>내 에서 <code>justify-content: center</code>, <code>align-items: center</code> .. <code>top</code>에서 50%, <code>left</code>에서 50% 위치시키고 정중앙 위치하도록 <code>transform</code> -50%씩… </p>
<p>요소를 가운데 정렬하기 위해 애쓰던 우리의 모습 기억나시나요?</p>
<p>차크라ui에는 자식 요소를 가운데에 정렬해주는 레이아웃 컴포넌트 <strong>Center</strong>가 있습니다.</p>
<img width="480" alt="스크린샷 2022-11-04 오후 11 05 11" src="https://user-images.githubusercontent.com/99077953/199992722-b9253762-b007-4968-b1ae-db185ea71cdc.png">


<h3 id="4-3-modal-컴포넌트">4-3. Modal 컴포넌트</h3>
<p>모달 컴포넌트가 하는 역할은 계속해서 반복합니다. </p>
<ul>
<li>모달 내용을 보여주고,</li>
<li>모달 닫기 버튼,</li>
<li>모달 뒤 배경은 투명하게</li>
<li>모달 외부 영역 클릭 시 작동하지 않게,</li>
</ul>
<p>이러한 기능을 몇번씩이나 구현했던 기능이 있습니다. 이러한 기능들을 반복했다는 사실을 차크라ui는 알고 있듯이 그대로 기능을 구현해줍니다. </p>
<ul>
<li>모달 내용을 보여주고, → <strong>ModalContent</strong></li>
<li>모달 닫기 버튼, → <strong>ModalCloseButton</strong></li>
<li>모달 뒤 배경은 투명하게 → <strong>ModalOverlay</strong></li>
<li>모달 외부 영역 클릭 시 작동하지 않게, → <strong>closeOnOverlayClick={false}</strong></li>
</ul>
<img width="539" alt="스크린샷 2022-11-04 오후 11 05 29" src="https://user-images.githubusercontent.com/99077953/199992944-14556592-7a51-406b-90c0-3758155b536a.png">


<p>자세한 내용은 <a href="https://chakra-ui.com/docs/components/modal/usage#make-modal-vertically-centered">공식 문서로</a></p>
<h2 id="5-그냥-내-생각">5. 그냥 내 생각</h2>
<p> 차크라ui를 영업 당하면서 매력적인 기능이 너무 많다고 느껴졌습니다. </p>
<p>-스타일 코드 때문에 코드 길이가 길어져 스타일 코드를 분리할지 말지 고민할 필요도 없습니다.</p>
<p>-<code>display: flex; justify-content: center, align-items: center</code> 를 매번 치면서 &#39;이거 진짜 어떻게 할 수 없나?&#39;라고 생각했는데 차크라ui가 가려운 부분을 긁어준 느낌이었습니다. </p>
<p>물론 새롭게 쓰는 툴인만큼 차크라ui를 사용하게 되면 css에 따른 prop 이름도 알아봐야 하고, 모든 UI가 마법처럼 와랄라라 구현될 것이라고 생각하지는 않습니다. 그럼에도 차크라가 속시원하게 가려운 부분을 콕 집어 해결해 주는 모습을 보면서 한 번 써볼만한 라이브러리란 생각이 들었고, 이는 다음 프로젝트 때 차크라 사용을 결정하게 된 이유가 되었습니다. 이 글을 통해서 이런 스타일 라이브러리가 있구나 봐주시면 좋을 것 같습니다!</p>
<p>참고 사이트
<a href="https://chakra-ui.com/">https://chakra-ui.com/</a>
<a href="https://www.youtube.com/watch?v=LnD5gMAJvcc">https://www.youtube.com/watch?v=LnD5gMAJvcc</a></p>
<hr>
<p><strong>작성자</strong>
<a href="%5Bhttps://github.com/choichoijin%5D(https://github.com/choichoijin)">IN SOPT OB 웹 최유진</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[구글링 잘하는 개발자 되기]]></title>
            <link>https://velog.io/@sopt_official/%EA%B5%AC%EA%B8%80%EB%A7%81-%EC%9E%98%ED%95%98%EB%8A%94-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%90%98%EA%B8%B0</link>
            <guid>https://velog.io/@sopt_official/%EA%B5%AC%EA%B8%80%EB%A7%81-%EC%9E%98%ED%95%98%EB%8A%94-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%90%98%EA%B8%B0</guid>
            <pubDate>Tue, 24 Jan 2023 10:13:22 GMT</pubDate>
            <description><![CDATA[<h1 id="구글링-잘하는-개발자-되기">구글링 잘하는 개발자 되기</h1>
<p><img src="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/223d806f-0c2e-4e4b-93c0-b950fdab6eb0/Untitled.png" alt="개발자들의 알파이자 오메가인 개발 지식 문답 사이트 ‘스택 오버플로우’"></p>
<p>개발자들의 알파이자 오메가인 개발 지식 문답 사이트 ‘스택 오버플로우’</p>
<p>  개발하며 겪는 에러를 해결하기 위해 <code>구글링</code>은 정말 땔래야 땔 수 없는 존재입니다. 저 역시도 개발을 처음배울때, 주변 친구들에게 가장 많이 들었던 소리가 “구글에는 검색해봤어?” ,“모르면 꼭 물어봐” 같은 말이었던 것 같아요. 제가 속한 개발 커뮤니티나 단체 톡방에서도 문답이 정말 활발하게 올라오고 있고, 이렇게 자유롭게 문답을 주고받으며 상호성장할 수 있다는 것은 개발이라는 분야의 매력이 아닌가 싶습니다.  </p>
<p><img src="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f9ad3c3b-b9ed-40ef-bdf6-27673f8e0ed8/Untitled.png" alt="예전에 인터넷에 화제됐던 인생 2회차 개발자"></p>
<p>예전에 인터넷에 화제됐던 인생 2회차 개발자</p>
<p>  다만 최근들어 입문자 입장에서는 개발이라는 분야가 생소하고 어떻게 구글링을 하고 어떻게 질문을 할지 익숙하지 않다보니, 여러 강의들의 질문게시판이나 단체 톡방을 보면 질문자체를 이해하기 힘들거나 내용파악이 어려운 질문들이 올라와 종종 불필요한 커뮤니케이션 로스가 일어나기도 합니다. 따라서 오늘은 ‘구글링을 잘하는 개발자’가 될 수 있도록 약간의 팁들을 소개해드리고자 합니다.</p>
<ul>
<li><strong>우리가 구글링을 하는 과정은 다음과 같습니다.</strong><ol>
<li>원하는 솔루션을 찾아</li>
<li>해당 솔루션을 이해하고</li>
<li>원하는대로 수정하여 기능을 구현</li>
</ol>
</li>
</ul>
<p>해당 아티클에서는 1번 과정에서 원하는 솔루션을 더 효율적으로, 빠르게 찾는 구글링 방법들을 소개합니다. </p>
<hr>
<h2 id="1-키워드의-노이즈-줄이기">1. 키워드의 노이즈 줄이기</h2>
<ul>
<li>최근 진행하고 있는 프로젝트에서 자바스크립트의 DATE 객체를 이용해 날짜를 얻고, 객체에서 얻어온 날짜 데이터를 Next.js의 라우팅과 연동하는 기능을 개발한다고 합시다. 이 기능을 구현하기 위해 어떻게 구글링을 해야할까요?</li>
</ul>
<blockquote>
<p>‘How can i handle routing in nextJS and Date Object in JavaScript’</p>
</blockquote>
<ul>
<li>이렇게 검색한다면 검색 결과에 노이즈가 생기고,  검색 결과가 너무 방대해지며, 최악의 경우에는 원하는 검색어가 나오지 않을 가능성이 있습니다. 따라서 위와같은 경우에는 불필요한 문법적 요소를 제외한 핵심 단어만 뽑아서 검색합니다.</li>
</ul>
<blockquote>
<p>JS Date Object (기술스택/원하는 것)</p>
</blockquote>
<blockquote>
<p>Next.js Routing (기술 스택/원하는 것)</p>
</blockquote>
<ul>
<li>이런식으로 문제를 쪼갠 후 얻고자하는 핵심 요소들만 키워드에 포함하여 검색하는 것이죠. 이런식으로 원하는 키워드를 선별하면 원하는 결과를 얻기 쉽습니다.</li>
</ul>
<hr>
<h2 id="2-구글링-문법-활용">2. 구글링 문법 활용</h2>
<ul>
<li>위의 예시처럼 키워드를 한정해도,  좀 더 자세하게 검색 결과를 얻고 싶은 경우 별도의 구글링 문법을 이용하여 검색합니다.</li>
</ul>
<h3 id="✅-정확한-검색어-찾기">✅ 정확한 검색어 찾기(””)</h3>
<p><img src="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c3a02494-45ec-4491-9c92-3d7d0a0c2955/Untitled.png" alt="Untitled"></p>
<ul>
<li>개발하는 경우 에러메시지는 대부분 유사한 형식을 갖고있어서, 원하는 결과가 바로 안나올 수 있습니다. 이럴 경우에 <code>큰따옴표(&quot;&quot;)</code> 를 이용해 메시지를 쪼개어 검색합니다.</li>
<li>에러 메시지에 따옴표가 붙어 나오는 경우에 그대로 copy &amp; paste를 할 경우 해당 문법이 적용되므로, 따옴표를 제거 후 구글링합니다.</li>
</ul>
<p><img src="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/73aea2ee-95f3-414d-85d4-d51b38e68141/Untitled.png" alt="Untitled"></p>
<ul>
<li>정확히 검색하고자하는 키워드가 여러 개인 경우 <code>+</code>키워드를 사용하여 여러개의 “”문법을 사용할 수 있습니다.</li>
</ul>
<h3 id="✅-특정-검색어-제외하기-">✅ 특정 검색어 제외하기(-)</h3>
<p><img src="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3dbee180-e72a-4b31-a470-caa32a2e535f/Untitled.png" alt="Untitled"></p>
<ul>
<li><code>-</code> 기호를 사용하면 검색 결과에서 특정 단어를 제외한 결과를 볼 수 있습니다.</li>
</ul>
<h3 id="✅--두-가지-단어-이상-동시-검색or">✅  두 가지 단어 이상 동시 검색(OR)</h3>
<p><img src="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0bd69608-465f-4d4a-a3d2-d7b7b74932e1/Untitled.png" alt="Untitled"></p>
<ul>
<li>특정 단어가 중복된 두 가지 이상의 검색 결과를 한번에 얻고싶은 경우, <code>OR</code> 키워드를 이용해 두가지 이상의 검색 결과를 한번에 얻을 수 있습니다.</li>
</ul>
<h3 id="✅--특정-사이트-내의-검색-결과만-가져오기-site">✅  특정 사이트 내의 검색 결과만 가져오기 (:Site)</h3>
<p><img src="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d99df4f1-2a00-4c1e-b119-60b407cf893e/Untitled.png" alt="Untitled"></p>
<ul>
<li><code>site:[사이트명]</code> 으로 검색할 시 해당 사이트의 검색 결과만 불러옵니다. 스택오버플로우 검색 결과만 조회하거나 깃허브에서 라이브러리 검색할때 유용합니다.</li>
</ul>
<h3 id="✅--특정-시간-범위-검색-결과만-가져오기-before-after">✅  특정 시간 범위 검색 결과만 가져오기 (before:, after:)</h3>
<p><img src="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3ab9722a-6867-479c-ac0d-0e140e1ae0ab/Untitled.png" alt="Untitled"></p>
<p><img src="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/98e50eef-0638-414c-8031-d6ecb8c33d80/Untitled.png" alt="Untitled"></p>
<ul>
<li>해당 스크린샷처럼 <code>YYYY-MM-DD</code>, <code>YYYY</code> 등의 형식으로 범위를 지정하여 검색할 수 있습니다. 필자의 경우 너무 오래된 자료들을 검색에서 제외할때 자주 사용합니다.</li>
</ul>
<h3 id="✅-파일타입-필터링-filetype">✅ 파일타입 필터링 [filetype:]</h3>
<p><img src="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9a3ea50e-b174-486d-80de-6b522d2abef8/Untitled.png" alt="Untitled"></p>
<ul>
<li>파일타입 필터를 통해 js, tsx, pdf, json 등의 특정 확장자로 끝나는 파일들만 검색할수있습니다.</li>
</ul>
<h3 id="✅--특정-검색어가-생각이-안날땐-와일드카드-">✅  특정 검색어가 생각이 안날땐 와일드카드 [*]</h3>
<p><img src="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b9e1cb51-adbf-4257-8632-ffc09e381f33/Untitled.png" alt="Untitled"></p>
<ul>
<li>‘아 그게 뭐였지 생각이 안나네…3 웨이 어쩌구저쩌구 쉐이킹이었는데’ 같이 문자열의 특정 단어만 생각나지 않을 경우 적용할 수 있는 문법입니다.</li>
</ul>
<hr>
<p>작성자
<a href="https://github.com/NamjunKim12">IN SOPT, OB 웹파트 김남준</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[개발 커뮤니티 운영진이 알려주는 개발 커뮤니티의 중요성]]></title>
            <link>https://velog.io/@sopt_official/%EA%B0%9C%EB%B0%9C-%EC%BB%A4%EB%AE%A4%EB%8B%88%ED%8B%B0-%EC%9A%B4%EC%98%81%EC%A7%84%EC%9D%B4-%EC%95%8C%EB%A0%A4%EC%A3%BC%EB%8A%94-%EA%B0%9C%EB%B0%9C-%EC%BB%A4%EB%AE%A4%EB%8B%88%ED%8B%B0%EC%9D%98-%EC%A4%91%EC%9A%94%EC%84%B1</link>
            <guid>https://velog.io/@sopt_official/%EA%B0%9C%EB%B0%9C-%EC%BB%A4%EB%AE%A4%EB%8B%88%ED%8B%B0-%EC%9A%B4%EC%98%81%EC%A7%84%EC%9D%B4-%EC%95%8C%EB%A0%A4%EC%A3%BC%EB%8A%94-%EA%B0%9C%EB%B0%9C-%EC%BB%A4%EB%AE%A4%EB%8B%88%ED%8B%B0%EC%9D%98-%EC%A4%91%EC%9A%94%EC%84%B1</guid>
            <pubDate>Tue, 24 Jan 2023 10:09:59 GMT</pubDate>
            <description><![CDATA[<h6 id="이-글은-지난-1년간-개발-커뮤니티에서-멤버와-코어-멤버로-활동한-뒤-쓰는-회고록의-성격이-가미된-아티클임을-서두에-밝힙니다-따라서-주관적인-글이-될-수-있으며-글의-내용에-대한-생각과-판단은-전적으로-독자의-몫에-있음을-알립니다">이 글은 지난 1년간 개발 커뮤니티에서 멤버와 코어 멤버로 활동한 뒤 쓰는 회고록의 성격이 가미된 아티클임을 서두에 밝힙니다. 따라서 주관적인 글이 될 수 있으며 글의 내용에 대한 생각과 판단은 전적으로 독자의 몫에 있음을 알립니다.</h6>
<p>이 글을 읽는 분들께서는 &#39;개발자&#39; 하면 어떤 이미지가 떠오르시나요? 
혹시 한 손에는 맥북을 다른 한 손에는 아이스 아메리카노를 들고 조용한 회사 사무실에서 열심히 개발하는 모습이 떠오르지는 않으셨나요? 
사실 제가 가진 개발자에 대한 이미지는 이런 모습이었습니다. 달리 말해 내가 맡은 일에만 충실하면 되는 모습이었어요. 
개발자는 개발만 잘하면 되지. 개발 실력 키우기에도 시간이 없는데. 회사 사람들하고만 잘 지내면 되는 거지. 나는 원래 사교적인 사람이 아니야. 
이 모든 것은 제가 그동안 가졌던 개발자에 대한 편견이자 선입견이었습니다. 여러분도 이 중 하나라도 그렇다 라고 생각하셨다면, 이 글을 끝까지 읽어주세요. 
왜 개발자가 커뮤니티에서 활동해야 하는지, 왜 사람들과 함께 어울리는 것에 익숙해 져야하는지 개발 커뮤니티의 장점을 소개하며 이야기해 보겠습니다.</p>
<p>개발자가 개발 커뮤니티를 하게 되면 얻을 수 있는 장점은 정말 많지만, 대표적인 3가지를 꼽는다면, </p>
<ol>
<li><p>내 실력으로는 잡기 힘든 기회를 예상치 못한 기회로 잡을 수 있다. </p>
</li>
<li><p>평소 관심 있는 분야부터 새로운 분야까지, 핵심적인 정보들을 생생히 전해들을 수 있다. </p>
</li>
<li><p>공모전, 해커톤 등에 나갈 동료를 구하기 쉽다. 정도인 것 같습니다.</p>
</li>
<li><p>내 실력으로는 잡기 힘든 기회를 예상치 못한 기회로 잡을 수 있다.
개발 커뮤니티에 가입되어 있으면, 유명한 연사분들을 뵐 기회가 생기게 됩니다. 
저의 경우 지난 여름 aws 에서 오신 연사분들을 만나 뵈었는데, 이때 연사분의 소개로 aws 에서 열리는 다른 행사에 참여할 수 있었고, 그 외의 다른 여러 프로그램들도 소개받을 수 있었습니다. 
그러면서 일반 학부생으로써는 만나기 힘든 분들도 많이 뵐 수 있었고, 그분들께 삶의 인사이트, 커리어의 방향성등을 상담받을 수 있어 큰 성장의 기회로 삼을 수 있었습니다. 
운영진으로 활동한 뒤로는 직접 현직자분들께 컨택하며 친분을 쌓고, 초청을 통해 강연을 들으면서 양질의 정보를 다수 얻을 수 있었습니다.</p>
<p> TIP! 특히 운영진이나 TF로 활동하게 될 경우, 연사님들과의 추가적인 네트워킹도 기대해 볼 수 있습니다! 이때 적극적으로 자신을 알리고, 궁금했던 것을 질문하면 좋은 인상을 남길 수 있는 것 같습니다. </p>
</li>
<li><p>평소 관심 있는 분야부터 새로운 분야까지, 핵심적인 정보들을 생생히 전해들을 수 있다.
개발 커뮤니티는 완전히 새로운 분야에 대한 최신 정보와 대두되는 화제들을 전해듣고 그 분야에 대한 빠른 탐색이 가능합니다. 
책을 찾아서 혼자 공부한다면 깊이 있게 공부할 수 있을진 모르지만 최신 동향을 파악하기는 쉽지 않습니다. 
빠르게 변화하는 개발 세계에서 자신이 흥미 있는 새로운 분야에 대한 정보는 현직자에게 듣는 것이 가장 효과적이고 효율적인 학습 방법이라고 생각합니다. 
물론 개발 커뮤니티에서 깊이 있는 내용까지 공부하지는 못하지만, 적어도 그 분야가 무엇이며 어떤 흐름으로 흘러가는지에 대한 방향을 현직자의 관점에서 볼 수 있다면 다른 사람들보다 훨씬 더 빠르게 해당 분야에 대한 감을 잡을 수 있다고 생각합니다.</p>
<p> 아울러 평소 자신이 관심있게 보았던 분야의 경력자들을 만나면서 지금까지 해온 공부의 방향을 점검받고, 최신 채용 동향에 대해서도 파악할 수 있습니다. 저의 경우 IBM에서 근무하신 소프트웨어 엔지니어분을 만날 기회가 있었는데, 이때 cs 공부의 중요성에 대해 현직자의 관점에서 전해들을 수 있어 이후 전공 공부의 효용성을 느끼며 공부하는 데 큰 도움이 되었습니다.</p>
</li>
<li><p>공모전, 해커톤 등에 나갈 동료를 구하기 쉽다.
마이크로소프트에서 열린 걸스인텍 밋업에서 현직 프론트엔드 개발자분들과 네트워킹할 시간이 있었습니다. 
이처럼 개발 커뮤니티 내에서 네트워킹 시간을 가질 때 링크드인으로 깃허브 주소등을 공유해 두면, 네트워킹 때 친분을 쌓아둔 분으로부터 공모전이나 해커톤에 같이 참여하자는 제의를 받을 확률이 높아집니다. 
반대로 자신의 깃허브를 널리 공유하며 동료를 찾을 수도 있습니다. 
개발 커뮤니티의 성격에 따라 차이가 있겠지만, 많은 사람이 모인 커뮤니티일수록 공모전, 해커톤에 나갈 동료를 구하기 쉬운 경향이 있는 것 같습니다.</p>
</li>
</ol>
<h2 id="결론">결론</h2>
<p>개발 커뮤니티에 처음 발을 들이면서 시간만 낭비하지 않을까. 얻는 것이 있을까. 하는 고민을 한 적이 있습니다. 그러나 1년간의 활동을 마친 지금은 생각이 완전히 달라졌습니다. 우선 개발 커뮤니티의 경우 다른 활동에 부담없이 진행되는 경우가 많고, 꼭 유명 연사분들 강연이 아니더라도 다양한 사람들이 어떻게 커리어를 쌓아왔는지 듣다 보면 자신의 커리어 패스 설계에 분명하게 도움이 됨을 느꼈습니다. 그리고 위와 같은 장점들을 포함해 성장을 도모하는 사람들과 함께 한다는 소속감은 하나의 동기가 되어 개발을 더욱 잘하게 되는 계기가 되기도 합니다. </p>
<h3 id="추가로-제가-알고-있는-몇몇-국내-개발-커뮤니티-소개하며-마무리하겠습니다">추가로, 제가 알고 있는 몇몇 국내 개발 커뮤니티 소개하며 마무리하겠습니다.</h3>
<ol>
<li>AUSG
AWS, 클라우드 등에 대한 관심있는 사람들이 모인 커뮤니티로, 주된 관심사는 aws와 클라우드이나 다양한 백그라운드를 가진 개발자들이 모이는 커뮤니티</li>
<li>GDSC, GDG
구글에서 서포트하는 대표적인 개발자 커뮤니티로, GDSC 는 대학생들이 자발적으로 형성, GDG 는 직장인들이 형성하는 커뮤니티. 대학별, 지역별로 나뉘어져 있으며 전세계적으로 활성화 되어 있어 다양한 기회가 오가는 커뮤니티입니다.</li>
<li>Girls in Tech
IT, Tech 분야의 여성들의 모임과 결속을 목표로 하는 커뮤니티, 다만 남자라고 해서 가입이 불가한 것은 아닌 듯 합니다. wanted 에서 종종 여러 행사를 열고 있으며 디스코드에서 알고리즘 문제 풀이도 진행하고 있습니다.</li>
<li>[기술명] 한국 사용자 모임 ex) 파이토치 한국 사용자 모임
말 그대로 해당 기술을 사용하는 한국 사용자들이 모여 잇는 커뮤니티. 산업체에 계신 현직자분들, 경력자분들께서 많은 인사이트를 주신다고 알고 있습니다. 관심있는 기술이 있다면 한국 사용자 모임에 가입해 보시는 것을 추천드립니다.</li>
</ol>
<hr>
<p>작성자
<a href="https://github.com/borimong?tab=repositories">IN SOPT YB WEB 김현수</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[비동기 그리고 에러처리]]></title>
            <link>https://velog.io/@sopt_official/%EB%B9%84%EB%8F%99%EA%B8%B0-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%97%90%EB%9F%AC%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@sopt_official/%EB%B9%84%EB%8F%99%EA%B8%B0-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%97%90%EB%9F%AC%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Mon, 23 Jan 2023 06:44:20 GMT</pubDate>
            <description><![CDATA[<h2 id="비동기-처리를-사용하는-이유">비동기 처리를 사용하는 이유</h2>
<p>비동기 처리는 앞선 작업이 끝난 후 다음 작업이 실행되는 동기와 달리, 앞선 작업의 완료 여부와 관계 없이 후에 이어지는 작업을 하는 방식입니다. </p>
<p>왜 작업을 순서대로 처리하지 않느냐 ? 라고 물으신다면, 작업을 한 개씩 처리하는 방식은, 업무는 단순할 수 있으나 자원이 비효율적으로 사용되기 때문입니다.</p>
<p>예를 들어, 일상에서 어떠한 프로그램을 다운하는 데 10분이 걸린다고 생각해봅시다. 우리는 자연스레 프로그램이 다운로드 되는 동안 다른 할 일을 할텐데요, 이것이 바로 <strong>비동기 처리</strong>입니다. 프로그램이 다운로드 되는 시간동안 우리는 동시에 다른 일을 하고 있으니까요 !</p>
<p>10분이 걸리는 다운로드를 동기적으로 처리한다면, <strong>우리는 다운로드가 완료될 때까지 아무것도 못하고 하염없이 기다리게 될 것입니다</strong>. 비효율적이죠 ?</p>
<p>따라서, 웹 상에서 파일을 받아오거나, 데이터베이스 처리를 하는 등 시간이 상대적으로 오래 걸리는 작업을 할 때에는 비동기 처리를 할 수 있도록 하여, 오랜 시간 작업이 진행되는동안 다른 일을 할 수 있도록 합니다.</p>
<hr>
<h3 id="그러나-동기적-처리가-필요할-때가-있다-">그러나 동기적 처리가 필요할 때가 있다 ?</h3>
<p>위의 글을 읽어보면 그러면 동기는 왜 있나요 ? 할 수 있지만, 동기적 처리도 꼭 필요한 경우가 있습니다. A 작업의 결과인 a 정보를 사용하여 B에서 작업을 처리한다고 가정한다면, A 작업이 완료되지 않은 채 B 작업을 시작할 시 우리는 올바른 작업을 진행할 수 없겠죠 ? </p>
<p>따라서 우리는 상황에 따라 <strong>동기와 비동기를 적절히 사용</strong>할 줄 알아야합니다.</p>
<hr>
<h2 id="promise-란-">Promise 란 ?</h2>
<p>콜백 함수를 사용하지 않고 비동기 처리를 하기 위해 JS에서 제공하는 object로, 성공과 오류에 대한 응답 처리로 이루어져 있습니다</p>
<p>우리는 promise를 사용할 때 , <code>.then</code>, <code>.catch</code>를 사용한다는 것을 세미나 때 배웠습니다. + <code>finally</code>까지 해서 조금 더 자세히 알아봅시다.</p>
<ul>
<li><code>.then</code> : 비동기 처리가 성공할 경우, resolve 내의 값을 받아온다</li>
<li><code>.catch</code> : 비동기 처리가 실패하였을 때, reject 내의 값을 받아온다</li>
<li><code>.finally</code> : 비동기 처리의 성공 여부와 관계 없이 실행하고자 하는 기능을 넣어준다</li>
</ul>
<pre><code class="language-jsx">const promise = new Promise((resolve, reject) =&gt; {
  if (condition) {
    resolve(&quot;비동기 처리 성공&quot;);
  } else {
    reject(&quot;비동기 처리 실패&quot;);
  }
});

//* 비동기 처리 성공(then), 비동기 처리 실패(catch)
promise
    .then((resolvedData): void =&gt; console.log(resolvedData))
    .catch((error): void =&gt; console.log(error));
    .finally(() =&gt; {console.log(&quot;finally&quot;)})</code></pre>
<p>자 위와 같은 코드가 있다고 가정해봅시다. 각각 condition이 <code>true</code>/<code>false</code>일 때의 실행 결과는 아래와 같습니다. </p>
<p>비동기 처리의 성공 여부와 관계 없이 <code>finally</code> 안의 코드가 실행되는 것을 확인할 수 있죠 !</p>
<p><img src="https://velog.velcdn.com/images/sopt_official/post/44bbdab9-be65-46d6-a304-525473b597ad/image.png" alt=""></p>
<hr>
<h3 id="promise-chaining에서-catch는-여러-번-써주어야-할까-">promise chaining에서 .catch는 여러 번 써주어야 할까 ?</h3>
<p>정답은 <strong>NO</strong> 입니다. </p>
<pre><code class="language-tsx">promise
.then()
.then()
.then()</code></pre>
<p>위와 같은 promise chaining 코드에서 에러는 어디에서 발생할 지 모르죠.
이러한 경우 그러한 <strong><em>에러를 잡기 위해 모든 <code>.then</code> 다음에 <code>.catch</code>를 적어주어야하냐 ?</em></strong> 하면 *<em>아닙니다! *</em></p>
<p><code>catch</code>는 한 번의 사용으로 연결된 모든 promise에 대한 reject를 해결합니다. 
따라서, 아래와 같이 한 번만 적어주셔도 괜찮습니다.</p>
<pre><code class="language-tsx">promise
.then()
.then()
.then()
.catch() // 이렇게 한 번만 써줘도 된다 !!</code></pre>
<h3 id="error-반환-방법">Error 반환 방법</h3>
<p>우리는 위에서 reject 내에 error 시 반환할 문자열을 넣고, <code>console.log</code> 를 해주었습니다. (세미나 코드에서는 reject 내에 <code>new Error(문자열)</code> 형태로 되어있죠 !)</p>
<p>그러나, error을 <code>console.log</code>를 통해 출력해주는 것은 좋은 방식이 아닙니다. 
다음 세 개의 문서에서 제가 소개할 내용에 대해 보다 자세한 내용을 다루고 있으니 참고 부탁드립니다. (전 아주 간략 of 간략으로 소개드릴 것 ,,)</p>
<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Error">Error 객체</a></p>
<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/throw">throw</a></p>
<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/try...catch">try-catch</a></p>
<p>짜잔,,</p>
<pre><code class="language-jsx">promise
.then((resolvedData): void =&gt; console.log(resolvedData))
.catch((error): void =&gt; {throw new Error(error)})
.finally(() =&gt; {console.log(&quot;finally&quot;)});</code></pre>
<p><code>.catch</code> 내의 (error)은 우리가 reject 내에 적어준 문자열일 것이며, 우리는 이를 Error 객체로 묶어 throw로 던져줍니다 ! 
throw는 의도적으로 에러를 직접 발생시키는 것이에요</p>
<p>세미나 때의 코드를 그대로 실행시킨다면 아래와 같이 Error가 보일텐데요,</p>
<p><img src="https://velog.velcdn.com/images/sopt_official/post/20e27ae1-6e0a-435b-aa45-31671dcbe352/image.png" alt=""></p>
<p>위의 코드를 실행시키면 아래와 같이 뜹니다. 에러가 발생한 줄이 다르죠 ?? 위 결과에서 나타내는 11번째 줄은 <code>reject(new Error(”~~”))</code>가 있는 줄, 아래 결과의 18번째 줄은 <code>.catch((error): void =&gt; {throw new Error(error)})</code> 가 있는 줄입니다 !</p>
<p><img src="https://velog.velcdn.com/images/sopt_official/post/e7828388-23de-4649-bf99-8b3f4b5972ba/image.png" alt=""></p>
<p>그렇다면 세미나 코드에서 <strong>조금 더 깔끔하게 Error을 출력하는 방법</strong>이 없을까 ! 한다면 <code>error.name</code>과 <code>error.message</code>에 대해 알고 계셔야 하는데요, </p>
<p>Error : 비동기 처리 실패 에서 <code>Error</code>는 <code>error.name</code>, 비동기 처리 실패 는 <code>error.message</code>에 해당합니다.</p>
<pre><code class="language-jsx">.catch((error): void =&gt; console.log(error.name,&#39;:&#39;,error.message))</code></pre>
<p>따라서 위와 같이, <code>error.name</code>과 <code>error.message</code>만을 출력하게 해준다면 아래와 같이 깔꼼하게 볼 수 있습니다 !
<img src="https://velog.velcdn.com/images/sopt_official/post/22d57fcd-ff93-4b94-9e57-640b50c37454/image.png" alt=""></p>
<hr>
<p><strong>작성자</strong>
<a href="https://github.com/Tjdnjs">IN SOPT, YB Server 박서원</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입단언에 대하여]]></title>
            <link>https://velog.io/@sopt_official/%ED%83%80%EC%9E%85%EB%8B%A8%EC%96%B8%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC</link>
            <guid>https://velog.io/@sopt_official/%ED%83%80%EC%9E%85%EB%8B%A8%EC%96%B8%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC</guid>
            <pubDate>Mon, 23 Jan 2023 06:35:19 GMT</pubDate>
            <description><![CDATA[<h2 id="개요">개요</h2>
<blockquote>
<p>non-null assertion과 optional chaining을 알아보고 두 차이점을 간단히 비교해보자!</p>
</blockquote>
<p><img src="https://user-images.githubusercontent.com/82744423/193490050-5e34a464-ca29-440a-9741-8e1abe666dc8.png" alt="https://user-images.githubusercontent.com/82744423/193490050-5e34a464-ca29-440a-9741-8e1abe666dc8.png"></p>
<p>과제를 수행하다가 <code>Object is possibly &#39;undefined&#39;</code>를 마주하게 되었습니다. 이 오류가 일어나는 이유는 타입스크립트는 구체적인 value type을 예상하는데, 작성자가 구체적이지 못한 value type을 제공하기 때문인데요. <del>(쳇, 깐깐하기는)</del></p>
<p>작성자는 조건문과 non-null assertion, optional chaining을 활용해서 해결했는데, 이 포스팅에서는 <strong>non-null assertion</strong>과 <strong>optional chaining</strong>에 주목하려고 합니다.</p>
<hr>
<h2 id="non-null-assertion">non-null assertion</h2>
<p><em>post-fix 연산자인 <code>!</code> 는 앞의 값이 확실히 <code>null</code>이나 <code>undefined</code>가 아니라는 걸 알리려고 할 때 쓴다.</em></p>
<p>다시 말해서 <strong>&#39;나를 믿어. 절대 <code>null</code>이나 <code>undefined</code>일리 없어!</strong>&#39;라고 못 박아서 얘기한다고 생각하시면 됩니다.</p>
<pre><code class="language-tsx">  organize(members) {

    const ob = members.find((member) =&gt; member.group == &quot;ob&quot;);
    const yb = members.find((member) =&gt; member.group == &quot;yb&quot;);

    console.log(
      `오늘 서버파트 저녁 모임은 ${ob!.name},${yb!.name}입니다!`
    );
  }
</code></pre>
<hr>
<h2 id="optional-chaining">optional chaining</h2>
<p><em><code>?.</code>은 <code>?.</code>&#39;앞’의 평가 대상이 <code>undefined</code>나 <code>null</code>이면 평가를 멈추고 <code>undefined</code>를 반환한다.</em></p>
<pre><code class="language-tsx">  organize(members) {

    const ob = members.find((member) =&gt; member.group == &quot;ob&quot;);
    const yb = members.find((member) =&gt; member.group == &quot;yb&quot;);

    console.log(`오늘 서버파트 저녁 모임은 ${ob?.name},${yb?.name} 입니다!`);
  }
</code></pre>
<hr>
<h2 id="차이점">차이점</h2>
<p>동작은 비슷해보이지만 약간의 차이점이 있습니다.</p>
<p>다시 정리해보자면,
<strong>non-null assertion</strong>은  <code>null</code>, <code>undefined</code> 일 경우는 없음을 확신할 때 사용하며 
<strong>optional chaining</strong>은 <code>null</code>, <code>undefined</code> 이면 <code>undefined</code>을 리턴하는 것입니다.</p>
<p><strong>non-null assertion</strong>의 경우, 속성 체인을 <code>null</code>로 보호하지 않습니다!
이미 TypeScript에서 값이 <code>null</code>이 되지 않을 것이라고 단언해버렸기 때문이죠.
그래서 실제 속성이 <code>null</code> 일 경우 에러가 발생하게 됩니다.</p>
<p><strong>optional chaining</strong>의 경우, 속성이 null인지 아닌지 체크를 합니다. 
속성이 <code>null</code> 이거나 <code>undefined</code>으로 확인되면 TypeScript는 속성 체인 실행을 더 이상 중지하고 코드를 계속 진행하기에 속성값이 <code>null</code>, <code>undefined</code>여도 에러가 발생하지 않습니다.</p>
<hr>
<h2 id="마치며">마치며</h2>
<p><strong>non-null assertion</strong>과 <strong>optional chaining</strong>에 대해서 간단하게 알아보았습니다. 
지난 기수 때 많이 마주했던 <code>?</code>와 <code>!</code>였지만 그렇구나하고 넘어갔는데 이번 기회에 저도 어느정도 정리가 된 것 같아요!</p>
<p>충분히 좋은 기능들이라고 생각하지만 무조건 남용하는 건 지양하도록 해요🙌
non-null assertion은 타입 단언이기에 조심해서 사용되어야 하며,
Typescript의 장점이 묻힐 수도 있습니다. <del>그리고 eslint가 싫어한다고 합니다</del></p>
<p>또한 optional chaining의 경우, 안정적인 코드가 될 수도 있겠지만 테스트 코드를 작성할 때 사용한다면 디버깅 시 많은 어려움을 겪을 수 있어요!</p>
<p>이는 테스트의 목적을 방해할 수도 있겠죠? 
그러니 상황에 따라 신중하게 사용해보는 건 어떨까요?</p>
<hr>
<h2 id="참고-자료">참고 자료</h2>
<p><a href="https://www.notion.so/non-null-assertion-optional-chaining-aeb972e99de2411b934fac9515eb4705">Optional Chaining vs Assertion Operator in TypeScript</a></p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining">Optional Chaining</a></p>
<p><a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html">Typescript handbook</a></p>
<hr>
<p><strong>작성자</strong>
<a href="https://github.com/m1njae">IN SOPT, OB Server 강민재</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 변경에 유연하게 대응하는 컴포넌트 만들기]]></title>
            <link>https://velog.io/@sopt_official/React-%EB%B3%80%EA%B2%BD%EC%97%90-%EC%9C%A0%EC%97%B0%ED%95%98%EA%B2%8C-%EB%8C%80%EC%9D%91%ED%95%98%EB%8A%94-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@sopt_official/React-%EB%B3%80%EA%B2%BD%EC%97%90-%EC%9C%A0%EC%97%B0%ED%95%98%EA%B2%8C-%EB%8C%80%EC%9D%91%ED%95%98%EB%8A%94-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Sun, 01 Jan 2023 14:09:39 GMT</pubDate>
            <description><![CDATA[<h1 id="✨-들어가며">✨ 들어가며</h1>
<p>여러분은 컴포넌트를 어떤 기준으로 분리하나요?</p>
<p>저는 코드가 너무 길어지거나, 중복이 많은 부분을 컴포넌트로 분리하는 것 같아요.</p>
<p>그런데 이 방법이 과연 맞는 방법인지, 다른 사람들은 어떤 식으로 컴포넌트를 만드는 지에 대해 궁금해졌어요.</p>
<p>좋은 컴포넌트는 어떻게 만들 수 있는지 <a href="https://www.youtube.com/watch?v=fR8tsJ2r7Eg"><a href="https://www.youtube.com/watch?v=fR8tsJ2r7Eg">토스 | 지속 가능한 성장과 컴포넌트</a></a> 를 보고 정리한 것을 공유하고자 합니다.</p>
<h1 id="✨-변경에-대응할-수-있는-컴포넌트">✨ 변경에 대응할 수 있는 컴포넌트</h1>
<p>제품을 성장시키기 위해서 제품의 변경은 필연적입니다.</p>
<p>제품의 변경사항은 이전에 놓쳤던 고객의 니즈를 발견한 것과 같아요.</p>
<p>우리는 특정 프로덕트의 사용자에 대해 모르기 때문에 어떤 니즈가 있는지, 어떤 변경이 발생할 지 예측할 수 없습니다. </p>
<p>따라서 변경을 예측하기 보다는 대응해야 합니다.</p>
<h2 id="제품이-만들어지는-흐름">제품이 만들어지는 흐름</h2>
<p>여러분은 어떤 기준으로 코드를 분리하나요?</p>
<p>다음과 같은 경우에 코드를 분리할 수 있을 것 같아요.</p>
<pre><code>- 중복되는 게 많아서
- 코드가 너무 크고 길어서</code></pre><p>하지만 이러한 이유로 코드를 분리하면 아주 많은 props를 컴포넌트에 주입해야 하고, 나중에 코드를 보면 이게 무엇을 의미하는 지 일일이 해석해야 하는 번거로움이 생길 거에요. </p>
<p>또 다른 사람이 내 코드를 보면 절대 이해하지 못할 지도 모릅니다.</p>
<p>만들다보니 페이지가 너무 커져서, 적당히 덜어내다 보니 이러한 결과를 낳게 됩니다.</p>
<h2 id="변경에-대응하는-컴포넌트-만들기">변경에 대응하는 컴포넌트 만들기</h2>
<p>그렇다면 변경에 유연하게 대응하도록 짜여진 컴포넌트는 어떤 특징을 갖고 있을까요?</p>
<ol>
<li><strong>Headless 기반의 추상화하기</strong> : 변하는 것 vs 상대적으로 변하지 않는 것</li>
<li><strong>한 가지 역할만 하기</strong> : 또는 한 가지 역할만 하는 컴포넌트의 조합으로 구성하기</li>
<li><strong>도메인 분리하기</strong> : 도메인을 포함하는 컴포넌트와 그렇지 않는 컴포넌트 분리하기</li>
</ol>
<p>다음과 같은 3가지 특징을 가진 컴포넌트를 차례대로 살펴볼게요.</p>
<h2 id="1-headless-ui-기반의-추상화하기">1. Headless UI 기반의 추상화하기</h2>
<p>컴포넌트는 크게 3가지의 역할을 합니다.</p>
<p><code>데이터</code> - <code>UI</code>- <code>사용자</code></p>
<p>컴포넌트는 데이터를 관리합니다. 그리고 UI(User Interface)를 통해 데이터를 어떻게 보여줄 지 관리해요.</p>
<p>어떻게 보여질 지 정의하는 부분은 디자인에 의존합니다. 
이렇게 디자인에 의존하는 UI를 컴포넌트가 관리하는 데이터로 분리해보면 어떨까요?</p>
<p><img src="https://user-images.githubusercontent.com/66051416/199696686-8d3b52ab-067c-486d-922b-bf702db5b9f5.png" alt="image"></p>
<p>달력 컴포넌트를 만든다고 가정하면, 데이터 자체는 변하지 않지만 UI는 언제든지 변경될 수 있습니다. 
따라서 변경에 유연하게 대응하기 위해 이 둘을 분리해보면 좋을 것 같아요.</p>
<p>2x2 배열로 Date 객체를 만들 수 있어요. 현재 날짜에 대한 값도 함께 추상화해볼 수 있습니다.</p>
<p>그리고 데이터는 <code>useCalendar</code>라는 hooks로 관리할 수 있습니다. 이를 어떻게 보여줄 지, 즉 UI만 정의하면 됩니다.</p>
<pre><code class="language-jsx">export default function Calendar() {
  const { headers, body, view } = useCalendar();
  return (
    &lt;Table&gt;
      &lt;Thead&gt;
        &lt;Tr&gt;
          {headers.weekDays.map(({ key, value }) =&gt; {
            return &lt;Th key={key}&gt;{format(value, &quot;E&quot;, { locale })}&lt;/Th&gt;;
          })}
        &lt;/Tr&gt;
      &lt;/Thead&gt;
      &lt;Tbody&gt;
        {body.value.map(({ key, value: days }) =&gt; (
          &lt;Tr key={key}&gt;
            {days.map(({ key, value }) =&gt; (
              &lt;Td key={key}&gt;{getDate(value)}&lt;/Td&gt;
            ))}
          &lt;/Tr&gt;
        ))}
      &lt;/Tbody&gt;
    &lt;/Table&gt;
  );
}
</code></pre>
<p>만약 변경사항이 생겨 디자인이 달라지더라도, 혹은 비슷한 컴포넌트이지만 디자인이 다르더라도 hook을 만들어 놓으면 가져다 쓰기만 하면 됩니다. 
달력을 구성하는 데 필요한 값을 계산하는 역할을 <code>useCalendar</code> hooks에 위임한 것으로 볼 수 있어요.</p>
<p>이와 같이 UI를 관심사에서 제외하여 오로지 데이터에만 집중해서 모듈화하는 것을 <code>Headless</code>라고 합니다. </p>
<hr>
<p>UI와 사용자가 상호작용 하는 부분도 분리해볼게요.</p>
<pre><code class="language-jsx">interface Props extends ComponentProps&lt;typeof Button&gt; {
  onLongPress?: (event: LongPressEvent) =&gt; void;
}
export function PressButton({ onLongPress, ...props }: Props) {
  return (
    &lt;Button
      onKeyDown={(e) =&gt; {
        // ...
      }}
      onKeyUp={(e) =&gt; {
        // ...
      }}
      onMouseDown={(e) =&gt; {
        // ...
      }}
      onMouseUp={(e) =&gt; {
        // ...
      }}
      {...props}
    /&gt;
  );
}
</code></pre>
<p>길게 꾹 누르는 onLongPress라는 상호작용을 정의하고 싶은데, 이미 많은 로직이 들어가있다면 컴포넌트 내부가 복잡하고 코드가 지저분해질 수 있어요. </p>
<p>이런 경우 onLongPress 로직을 컴포넌트 내부에서 정의하기 보다는, hooks로 분리하여 리턴값을 적용하기만 하면 UI와 데이터를 분리할 수 있습니다.</p>
<pre><code class="language-jsx">
interface Props extends ComponentProps&lt;typeof Button&gt; {
 onLongPress?: (event: LongPressEvent) =&gt; void;
}
export function PressButton(props: Props) {
 const longPressProps = useLongPress();
 return &lt;Button {...longPressProps} {...props} /&gt;
}
function useLongPress() {
 return {
    // ...
 )
}</code></pre>
<p>이렇게 로직을 따로 분리해두면 다른 컴포넌트에서 LongPress 이벤트를 적용하려고 할 때 hook을 가져다 쓰기만 하면 된다는 장점도 있어요.</p>
<p>hooks로 모듈화하는 내용을 길게 소개한 이유는 변경에 유연해지려면 각 모듈이 한 가지 일만 하는 것이 중요하기 때문입니다. </p>
<h2 id="2-composition">2. Composition</h2>
<p>그렇다면 복잡한 컴포넌트가 한 가지 역할만 하려면 어떻게 해야할까요?</p>
<p><img src="https://user-images.githubusercontent.com/66051416/199696776-12d9937c-6491-41a1-9c9f-9846386f470f.png" alt="image"></p>
<pre><code class="language-jsx">function DateSelect() {
  const [isOpen, setIsOpen] = useState(false);
  const [selected, setSelected] = useState(&quot;이번 주&quot;);
  const options = [&quot;오늘&quot;, &quot;이번 주&quot;, &quot;이번 달&quot;, &quot;올해&quot;];
  return (
    &lt;&gt;
      &lt;SelectButton value={selected} /&gt;
      {isOpen &amp;&amp; (
        &lt;Options onClick={() =&gt; setIsOpen(false)}&gt;
          {options.map((option) =&gt; {
            return (
              &lt;Button
                selected={selected === option}
                onClick={() =&gt; setSelected(option)}
              &gt;
                {value}
              &lt;/Button&gt;
            );
          })}
        &lt;/Options&gt;
      )}
    &lt;/&gt;
  );
}</code></pre>
<p>위 코드는 재사용이 어렵습니다. 만약 해당 컴포넌트를 다른 컴포넌트에서도 사용한다면 어떨까요?</p>
<p>일자를 선택하는 부분이 다른 컴포넌트로 바뀌거나, 새로운 기능을 하는 버튼이 추가되는 등의 변경이 생긴다면 대응이 어렵게 될 거에요.</p>
<pre><code class="language-jsx">function Select(props) {
  const { isOpen, trigger, value, onClick, options,handleIsOpen } = props;
  return (
    &lt;Dropdown value={value}&gt;
      &lt;Trigger&gt;{trigger}&lt;/Trigger&gt;
      &lt;Menu onClick={onClick} isOpen={isOpen}&gt;
        {options.map((option) =&gt; (
          &lt;Item key={option} isSelected={option === value} onClick={handleIsOpen}&gt;
            {option}
          &lt;/Item&gt;
        ))}
      &lt;/Menu&gt;
    &lt;/Dropdown&gt;
  );
}</code></pre>
<ul>
<li>메뉴의 노출 여부를 제어하는 상태인 <code>isOpen</code> -&gt;조건부 렌더링 말고 함수로 처리 , </li>
<li>상태를 바꾸기 위한 상호작용 <code>trigger</code> -&gt; Trigger, </li>
<li>옵션 영역 <code>menu</code> -&gt; Menu,</li>
<li>메뉴를 구성하는 각각의 아이템 <code>item</code> -&gt; Item</li>
</ul>
<p>이렇게 다루고 있는 데이터, 담당하고 있는 역할을 기준으로 분리해볼 수 있어요.</p>
<pre><code class="language-jsx">function Velog() {
  const [isOpen, setIsOpen] = useState(false);
  const [selectedDate, setSelectedDate] = useState(&quot;이번 주&quot;);
  const dates = [&quot;오늘&quot;, &quot;이번 주&quot;, &quot;이번 달&quot;, &quot;올해&quot;];

  const handleSelectedDate = (e) =&gt; {
    setSelectedDate(e.target.innerText);
  };

  const handleIsOpen = () =&gt; {
    setIsOpen((prev) =&gt; !prev);
  };

  return (
    &lt;Root&gt;
      &lt;Select
        trigger={&lt;DropBox value={selectedDate} handleIsOpen={handleIsOpen} /&gt;}
        isOpen={isOpen}
        value={selectedDate}
        handleIsOpen={handleIsOpen}
        onClick={handleSelectedDate}
        options={dates}
      /&gt;
    &lt;/Root&gt;
  );
}</code></pre>
<p>trigger로 전달한 <code>DropBox</code>컴포넌트와 <code>Select</code>컴포넌트는 서로의 존재를 알지 못합니다. 따라서 변경이 발생해도 서로 영향을 주지 않을 거에요.</p>
<p><img src="https://user-images.githubusercontent.com/66051416/199700397-52974005-aa21-4833-a4b7-95a93275d9a8.gif" alt="Vite_App_-_Chrome_2022-11-03_19-02-18_AdobeExpress"></p>
<p>이와 같이 합성(Composition)이 가능하도록 컴포넌트를 만들면 재사용하기도 좋고, 확장하는 데에도 좋아요.</p>
<h2 id="3-도메인-분리하기인터페이스를-일반화하자">3. 도메인 분리하기(인터페이스를 일반화하자)</h2>
<p>여기서 도메인은 다루고 있는 비즈니스(ex. velog의 일자별 드롭박스에서는 <code>일자별</code> 이 도메인)와 관련된 부분이라고 생각하면 됩니다.</p>
<p>컴포넌트를 주입받은 것처럼 데이터도 주입받으면 어떨까요?</p>
<p>‘Date’라는 도메인을 분리해볼게요.</p>
<p>다음과 같은 props를 전달했다고 가정하고 props 네이밍에서 도메인 맥락을 제거해볼게요.</p>
<pre><code class="language-jsx">interface Props {
- selectedDates: string[];
+ value: string[]
- onDateChange: (selecteds: string[]) =&gt; void;
+ onChange: (value: string[]) =&gt; void;
- datesOptions: Array&lt;{ label: string }&gt;;
+ options: Array&lt;{ label: string }&gt;
}</code></pre>
<p>도메인 맥락을 지우다보면 일반적인 이름으로 변경되고, 컴포넌트 인터페이스는 일반적일수록 이해하기 쉽습니다.</p>
<p>컴포넌트 네이밍, props 네이밍을 신중히 고민하는 연습이 필요할 것 같아요.</p>
<h2 id="action-item">Action Item</h2>
<p>좋은 코드, 컴포넌트를 작성하기 위한 두 가지 Action Item을 소개합니다.</p>
<h2 id="인터페이스를-먼저-고민하자">인터페이스를 먼저 고민하자</h2>
<p>이미 컴포넌트가 존재한다고 가정하고 해당 컴포넌트를 고민해보는 건 어떨까요.</p>
<ul>
<li><p><code>의도</code>가 무엇인가?</p>
</li>
<li><p>컴포넌트의 <code>기능</code>은 무엇인가?</p>
</li>
<li><p>어떻게 <code>표현</code>되어야 하는가?</p>
<p>이 3가지가 구현보다 중요한 요소들입니다. 변경하려고 할 때 파악해야 하는 것들이기 때문이에요.</p>
</li>
</ul>
<h2 id="컴포넌트를-나누는-이유에-대해-다시-생각하자">컴포넌트를 나누는 이유에 대해 다시 생각하자</h2>
<ul>
<li>컴포넌트로 빼면 실제로 <strong>복잡도를 낮추는지</strong> 생각하기</li>
<li>컴포넌트로 빼면 <strong>재사용 가능한</strong> 컴포넌트인지 고민하기</li>
<li><strong>꼭 분리해야 하는지</strong> 고민하기</li>
</ul>
<p>잘 만든 컴포넌트는 함께 일하는 동료에게도 도움이 될 수 있스빈다.</p>
<p>따라서 변경에 유연한 컴포넌트는 비즈니스를 안정적으로 만드는 데에도 도움이 됩니다.</p>
<h1 id="✨-마무리">✨ 마무리</h1>
<ul>
<li>컴포넌트가 변경에 유연하기 위한 세 가지 특징</li>
</ul>
<ol>
<li>Headless 기반의 추상화하기</li>
<li>한 가지 역할만 하기</li>
<li>도메인 분리하기</li>
</ol>
<ul>
<li>시도해볼 수 있는 Todo List</li>
</ul>
<ol>
<li>인터페이스 먼저 고민하기</li>
<li>분리하기 전 다시 생각하기
⑴ 복잡도를 낮추는가?
⑵ 재사용 가능한 컴포넌트가 만들어지는가?</li>
</ol>
<p><a href="https://www.youtube.com/watch?v=fR8tsJ2r7Eg"><a href="https://www.youtube.com/watch?v=fR8tsJ2r7Eg">토스 | 지속 가능한 성장과 컴포넌트</a></a> 를 보고 영상에 등장하는 수도코드를 해석해서 velog의 일자 선택 드롭박스 컴포넌트를 변경에 유연히 대응할 수 있게 구현해본 내용을 정리해보았습니다. 
앞으로 컴포넌트를 만들 때 무작정 구현하기보다는 컴포넌트의 기능과 분리에 대해 고민하고 설계해보는 시간을 가져야겠습니다.</p>
<hr>
<p><strong>작성자</strong>
<a href="https://github.com/leeseooo">IN SOPT WEB, OB 이서영</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ARC에 대하여]]></title>
            <link>https://velog.io/@sopt_official/ios2</link>
            <guid>https://velog.io/@sopt_official/ios2</guid>
            <pubDate>Fri, 30 Dec 2022 06:06:49 GMT</pubDate>
            <description><![CDATA[<h1 id="1-메모리-구조">1. 메모리 구조</h1>
<p>운영체제에서는 메모리에 프로그램을 위한 공간을 할당해줍니다!
그 공간을 총 4가지로 나눌 수 있는데욥</p>
<p><img src="https://velog.velcdn.com/images/hello_hidi/post/c16bd056-4b5b-42d0-a307-5ab27d0840f2/image.png" alt="https://velog.velcdn.com/images/hello_hidi/post/c16bd056-4b5b-42d0-a307-5ab27d0840f2/image.png"></p>
<h3 id="i-code-data-영역">i) code, data 영역</h3>
<blockquote>
<p>code영역: 실행할 프로그램의 코드가 저장되는 영역으로 텍스트 영역이라고도 부릅니다. CPU는 코드 영역에 저장된 명령어를 하나씩 가져가서 처리하게 됩니다.
-&gt; 간단히 우리가 작성한 소스 코드가 기계어 형태로 저장됨!</p>
</blockquote>
<blockquote>
<p>data영역: 프로그램의 전역 변수와 정적(static) 변수가 저장되는 영역입니다.
데이터 영역은 프로그램의 시작과 함께 할당되며, 프로그램이 종료되면 소멸합니다.</p>
</blockquote>
<h3 id="ii-heap-영역">ii) heap 영역</h3>
<blockquote>
<p>사용자에 의해 메모리 공간이 동적으로 할당되고 해제되는 영역</p>
</blockquote>
<ul>
<li>사용하고 난 후에는 반드시 메모리 해제를 해줘야 한다. 그렇지 않으면 memory leak이 발생한다.</li>
<li>유일하게 런타임 시에 결정되기 때문에 데이터의 크기가 확실하지 않을 때 사용</li>
<li>메모리의 낮은 주소에서 높은 주소의 방향으로 할당됩니다.</li>
<li>Swift에서는 closure, class 등 참조타입의 값이 힙에 자동 할당됨!</li>
</ul>
<h3 id="iii-stack-영역">iii) stack 영역</h3>
<blockquote>
<p>함수의 호출과 관계되는 지역 변수와 매개변수가 저장되는 영역</p>
</blockquote>
<ul>
<li>함수의 호출과 함께 할당되며, 함수의 호출이 완료되면 소멸합니다.</li>
<li>컴파일 타임에 결정되기 때문에 무한히 할당할 수 없다.</li>
<li>프로그램이 자동으로 사용하는 임시 메모리 영역이다.</li>
</ul>
<h2 id="heap-vs-stack">Heap vs Stack</h2>
<h3 id="각각의-장단점">각각의 장단점</h3>
<h3 id="q-언제-힙을-쓰고-언제-스택을-쓰나용">Q. 언제 힙을 쓰고, 언제 스택을 쓰나용?</h3>
<p><strong>데이터의 크기를 모르거나, 스택에 저장하기에 너무 큰 데이터인 경우 힙에 할당</strong></p>
<blockquote>
<p>물론 위에 경우는 클로저와 인스턴스처럼 자동으로 힙에 할당하는 것 외에 직접 할당 경우이당~
만약에 너무 많은 메모리를 스택에 할당하면 우리에게 다른 의미로 익숙한</p>
<p><img src="https://velog.velcdn.com/images/hello_hidi/post/7377365e-0933-415d-b376-c4b82cc6355d/image.png" alt="https://velog.velcdn.com/images/hello_hidi/post/7377365e-0933-415d-b376-c4b82cc6355d/image.png"></p>
</blockquote>
<p>자 이렇게 메모리 기초를 봤는데 일단 ARC를 보기 전에 옵제씨 사용 틀딱 시절엔 힙 영역에 어케 할당하고 해제했는지를 먼저 보려고 한다. 그 이유는 ARC를 왜? 써야 되는지를 알려면 불편함을 느껴야 되기 때문! 그럼 MRC에 대해 알아보장~</p>
<h1 id="2-mrc">2. MRC</h1>
<p><strong>Manual Retain Counter의 준말로 수동으로 RC를 계산하는 기법이다!</strong></p>
<h3 id="i-rc란">i) RC란?</h3>
<p><strong>RC는 Retain Counter(MRC에서) 에 준말이며, 참조횟수이다!</strong></p>
<blockquote>
<p>Swift에는 값타입과 참조타입이 있으면 참조타입이 이해가 안된다면, 저번에 작성한 블로그를 참고하면 될 거 같다잉~</p>
</blockquote>
<h3 id="ii-retiancount">ii) retianCount</h3>
<p>MRC에 경우 개발자가 직접 count를 해서인지 인스턴스에 대한 RC에 접근할 수 있다! <strong>.retainCount는 인스턴스의 RC에 접근할 수 있는 프로퍼티이다!</strong></p>
<h2 id="mrr로-rc-다루기">MRR로 RC 다루기</h2>
<h3 id="1-인스턴스를-새로-생성할-때">1) 인스턴스를 새로 생성할 때</h3>
<p>Objective-C에서 인스턴스를 새로 생성하는 방법에 아래와 같은 메서드들이 있다.</p>
<blockquote>
<p>alloc</p>
</blockquote>
<ul>
<li>new</li>
<li>copy</li>
<li>mutableCopy</li>
</ul>
<pre><code class="language-objectivec">//아래와 같은 방식으로 인스턴스를 새로 생성할 수 있다.
TestClass *test = [[TestClass alloc] init];</code></pre>
<p>여기서 인스턴스를 새로 생성하면 자동으로 RC가 +1이 된다. 어 그럼 자동으로 되는거자나? 왜 구라깜? 이제 수동적으로 증가시키는 걸 보여줄게용</p>
<h3 id="2-retain-메소드를-사용할-때">2) retain 메소드를 사용할 때</h3>
<p><strong>retain 메서드를 사용해서 우린 RC를 수동적으로 증가시킬 수 있다</strong></p>
<blockquote>
<p>Q. 그럼 언제 쓰냐?
A. 절대로 남발해서는 안된다. 사실 RC값이 늘어나는 경우는 정해져있다. 새로 인스턴스를 생성했을때랑 기존 인스턴스를 참조했을 때. 그러나 새로 인스턴스를 생성할 땐 자동으로 생성해주니까
=&gt; 기존의 인스턴스를 참조할 때 retain 메소드를 사용해 RC값을 사용한다</p>
</blockquote>
<pre><code class="language-objectivec">TestClass *test = [[TestClass alloc] init];
TestClass *test2 = test; // 기존 인스턴스 참조
[test retain]; // test RC +1</code></pre>
<h3 id="3-release-메소드를-사용할-때">3) release 메소드를 사용할 때</h3>
<p><strong>release 메서드를 사용해서 우린 RC를 수동적으로 감소시킬 수 있다</strong></p>
<pre><code class="language-objectivec">[test release];
test2 = nil;</code></pre>
<h3 id="결론">결론</h3>
<p>그냥 할당할때마다 옵제씨 시절에는 할당,해제를 주구장창<del>~</del> 수동으로 해줘야했다. 벌써 불편하자나 그래서 우린 드디어 ARC를 배우러 간다.</p>
<p>MRC결론 : 후 너네는 이런거 하지마라~</p>
<p><img src="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0802e16b-5838-4b1f-8e83-421c994f0961/Untitled.png" alt="Untitled"></p>
<p>드디어 ARC이다!!!!</p>
<h3 id="참조타입과-heap">참조타입과 Heap</h3>
<p>ARC를 제대로 배울라면 저번주차에서 다뤘던 참조타입과 메모리 구조에서 배운 Heap에 대한 이해가 있어야 된다!
간략하 설명하자면</p>
<blockquote>
<p>Swift의 클래스와 클로저는 Heap영역에 저장되는 참조타입이다.</p>
</blockquote>
<ul>
<li><p>참조타입의 특징은 지역변수는 단지 스택에 저장되어 인스턴스의 주소값을 가지고 있고</p>
</li>
<li><p>스택의 지역변수가 힙 영역의 실제 인스턴스를 참조하는 형태이다.</p>
<p>  <img src="https://velog.velcdn.com/images/hello_hidi/post/1a733fa6-d646-45d5-a46f-f4b6f6447269/image.png" alt="https://velog.velcdn.com/images/hello_hidi/post/1a733fa6-d646-45d5-a46f-f4b6f6447269/image.png"></p>
</li>
</ul>
<h3 id="메모리-해제">메모리 해제</h3>
<p>우리는 지금까지 걍 마음대로 클래스 만들어서 인스턴스 퐉퐈고파ㅗ가퐈 찍어내고 마음껏 사용했음. but Heap에 특징! 사용하고 난 후 반드시 메모리 해제를 해줘야 한다! 근데 한 사람????</p>
<p><img src="https://velog.velcdn.com/images/hello_hidi/post/8a019bce-2ad1-45d1-b2cb-db0a0f6f4d50/image.png" alt="https://velog.velcdn.com/images/hello_hidi/post/8a019bce-2ad1-45d1-b2cb-db0a0f6f4d50/image.png"></p>
<p>아무도 없을것이다! 왜냐? 바로 ARC가 해주기 때문이다!</p>
<h1 id="3-arc">3. ARC</h1>
<p><strong>Automatic Reference Counting의 약자로, 클래스 인스턴스가 더 이상 필요하지 않을 경우 메모리를 자동으로 해제해주는 것이 주기능이다!</strong></p>
<h3 id="arc-작동원리">ARC 작동원리</h3>
<blockquote>
<p>ARC는 인스턴스에 대한 정보를 저장하기 위해 메모리의 청크에 할당합니다.</p>
</blockquote>
<ul>
<li>메모리 청크: malloc()으로 할당 받는 영역과 header를 포함한 영역<blockquote>
</blockquote>
</li>
</ul>
<blockquote>
<p>컴파일 시점에 언제 참조되고 해제되는지 결정되어  retain, release를 삽입을 한다!</p>
</blockquote>
<ol>
<li><p>런타임 때 결정되어 그대로 실행한다</p>
<p> <img src="https://velog.velcdn.com/images/hello_hidi/post/25b17636-ee4d-4b97-b683-ee78987709fa/image.png" alt="https://velog.velcdn.com/images/hello_hidi/post/25b17636-ee4d-4b97-b683-ee78987709fa/image.png"></p>
</li>
</ol>
<h2 id="arc에서-rc-다루기">ARC에서 RC 다루기</h2>
<h3 id="i-rc값이-증가하는-경우">i) RC값이 증가하는 경우</h3>
<blockquote>
<p>인스턴스를 새로 생성할 때</p>
</blockquote>
<ul>
<li>기존 인스턴스를 다른 변수에 대입할 때</li>
</ul>
<h3 id="ii-rc값이-감소하는-경우">ii) RC값이 감소하는 경우</h3>
<blockquote>
<p>인스턴스를 가리키던 변수가 메모리에서 해제되었을 때</p>
</blockquote>
<ul>
<li>nil이 지정되었을 때</li>
<li>변수에 다른 값을 대입한 경우</li>
<li>프로퍼티의 경우, 속해있는 클래스 인스턴스가 메모리에서 해제될 때</li>
</ul>
<h3 id="예시-코드">예시 코드</h3>
<pre><code class="language-swift">class Ayomi {
    var name: String?
    var age: Int?

    init(name: String? = nil, age: Int? = nil) {
        self.name = name
        self.age = age
    }

    deinit {print(&quot;당신의 아요미 인생은 여기까지~&quot;)}
}

var hidi: Ayomi? = .init(name: &quot;hidi&quot;, age: 23) // hidi Instance RC: 1
var hidiclone = hidi // hidi Instance RC: 2, hidiclone Instance RC: 2

hidiclone = nil // hidi Instance RC: 1
hidi = nil // hidi Instance RC: 0

var min: Ayomi? = .init(name: &quot;min&quot;, age: 76) // min Instanc RC: 1
var minClone: Ayomi? = .init(name: &quot;minClone&quot;, age: 5) // minClone Instance RC: 1

min = minClone // min Instance RC: 0, minClone Instance RC: 2

class Ayo {
    var level: Int?
    var ayomi: Ayomi? = .init(name: &quot;ayomi&quot;, age: 20)

    init(level: Int? = nil) {
        self.level = level
    }
}

var ayo: Ayo? = .init(level: 31)
ayo = nil</code></pre>
<p>ARC의 단점 중 하나는 순환 참조가 발생 시 영구적으로 메모리가 해제되지 않을 수 있다. 아니 근데 저기요 순환 참조가 뭐죠? 그걸 알아보러 가보자잉~</p>
<h2 id="강한참조">강한참조</h2>
<p><strong>우리가 지금까지 했던 힙의 할당하는 방식이 강한 참조 방식이다!</strong></p>
<blockquote>
<p>즉 인스턴스의 주소값이 변수에 할당될 때, RC가 증가하면 강한 참조!
애초에 default 값이 strong~
하지만 strong에 문제는 바로 순.환.참.조</p>
</blockquote>
<h2 id="순환참조">순환참조</h2>
<p>예시를 들어보자! 요즘 내가 킹받고 있는 꽃보다 남자로 예시를 들어보겠다. 먼저 준표랑 잔디를 등장시켜보겠다</p>
<pre><code class="language-swift">class Man {
    var name: String
    var girlfriend: Woman?

    init(name: String) {
        self.name = name
    }
    deinit { print(&quot;맨킬뎃쉿스껄!&quot;) }
}

class Woman {
    var name: String?
    var boyfriend: Man?

    init(name: String) {
        self.name = name
    }
    deinit { print(&quot;우맨킬뎃쉿스껄!&quot;) }
}

var junpyo: Man? = .init(name: &quot;준표&quot;)
var zandi: Woman? = .init(name: &quot;잔디&quot;)</code></pre>
<p>그리고 이 둘은 서로가 서로의 남친 여친임을 표시해준다.
메모리에서 볼 때 둘의 관계는 아래 그림과 같다.</p>
<pre><code class="language-swift">junpyo?.girlfriend = zandi
zandi?.boyfriend = junpyo</code></pre>
<p>그러면서 이 둘의 RC도 하나씩 증가했다.</p>
<p><img src="https://velog.velcdn.com/images/hello_hidi/post/43dcd9d4-e5ef-4e42-8d0b-6b23c7dce098/image.jpeg" alt="https://velog.velcdn.com/images/hello_hidi/post/43dcd9d4-e5ef-4e42-8d0b-6b23c7dce098/image.jpeg"></p>
<h1 id="4-순환참조의-문제점">4. 순환참조의 문제점</h1>
<p>자~ 이제 순환참조의 문제점에 대해 알아보자! 이 메모리의 주인이 나는 둘의 사이가 너무 킹받아서 죽여버리기로 결심을 했따. 물론 메모리 상에서만 ㅎㅎ</p>
<pre><code class="language-swift">junpyo = nil
zandi = nil</code></pre>
<p>이렇게 하면 나의 예상대로 메모리가 깔끔하게 사라져야 되는데 현실은 RC의 값이 둘다 1임으로 메모리 상에 살아서 아직도 나를 킹받게 하는 것이다...</p>
<p><img src="https://velog.velcdn.com/images/hello_hidi/post/22c55e0f-3000-415f-a77a-427e0026eedf/image.jpeg" alt="https://velog.velcdn.com/images/hello_hidi/post/22c55e0f-3000-415f-a77a-427e0026eedf/image.jpeg"></p>
<p><strong>즉 strong으로 선언된 변수들이 순환참조 됐을 시 큰 문제는</strong></p>
<blockquote>
<p>서로가 서로를 참조하고 있어서 RC가 0이 되지 못한다는 것이다!</p>
</blockquote>
<ul>
<li>또한 해당 인스턴스를 가리키던 변수도 nil로 지정했기 때문에 인스턴스 접근도 할 수 없어 메모리 해제도 못하는 최악의 상황이 펼쳐진다.</li>
</ul>
<p>이것들을 해결하기 위해서 우리는 weak과 unowned를 알아야 한다.</p>
<h2 id="weak">weak</h2>
<p><strong>weak 약한참조, 즉 인스턴스를 참조할 시, RC를 증가시키지 않는다.</strong></p>
<blockquote>
<p>참조하던 인스턴스가 메모리에서 해제된 경우, 자동으로 nil이 할당되어 메모리가 해제된다!</p>
</blockquote>
<ul>
<li>weak은 프로퍼티를 선언한 이후, 나중에 nildㅣ 할당된다는 관점에서</li>
<li><em>무조건 옵셔널 타입의 변수*</em></li>
<li>둘 중에 수명이 더 짧은 인스턴스를 가리키는 애를 약한 참조로 선언함.</li>
</ul>
<pre><code class="language-swift">class Man {
    var name: String
    weak var girlfriend: Woman?

    init(name: String) {
        self.name = name
    }
    deinit { print(&quot;맨킬뎃쉿스껄!&quot;) }
}

class Woman {
    var name: String?
    var boyfriend: Man?

    init(name: String) {
        self.name = name
    }
    deinit { print(&quot;우맨킬뎃쉿스껄!&quot;) }
}

var junpyo: Man? = .init(name: &quot;준표&quot;)
var zandi: Woman? = .init(name: &quot;잔디&quot;)

junpyo?.girlfriend = zandi
zandi?.boyfriend = junpyo</code></pre>
<p><img src="https://velog.velcdn.com/images/hello_hidi/post/6824b330-04fa-486f-b9d9-3d49acc852dc/image.jpeg" alt="https://velog.velcdn.com/images/hello_hidi/post/6824b330-04fa-486f-b9d9-3d49acc852dc/image.jpeg"></p>
<blockquote>
<p>여기서 준표랑 잔디를 죽이면</p>
</blockquote>
<ol>
<li>zandi, junpyo가 스택에서 가리키는 인스턴스 RC 둘다 1</li>
<li>잔디의 RC가 0이 되면서 kill</li>
<li>준표의 여자친구 속성이 0이 되면서 RC 0되면서 킬</li>
<li>메모리 깨-끗&gt;</li>
</ol>
<h2 id="unowned">unowned</h2>
<p>weak과 비슷하게 RC값을 증가시키지 않아서 강한 순환 참조를 해결하지만,
<strong>인스턴스를 참조하는 도중에 해당 인스턴스가 메모리에서 사라질 일이 없다고 확신하는게 핵심임!</strong></p>
<blockquote>
<p>참조하던 인스턴스가 만약 메모리에서 해제된 경우에도 nil을 할당받지 못하고, 해제된 메모리 주소값을 계속 들고 있음</p>
</blockquote>
<ul>
<li>둘 중에 수명이 더 긴 인스턴스를 가리키는 애를 unowned 선언해야됨!</li>
</ul>
<pre><code class="language-swift">//week으로 선언될 경우
junpyo?.girlfriend / 결과값 : nil
//unowned으로 선언될 경우
junpyo?.girlfriend / 결과값 : error: Execution was interrupted
</code></pre>
<p><strong>작성자</strong>
<a href="https://github.com/HELLOHIDI">IN SOPT, YB iOS 류희재</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Nextjs 톺아보기]]></title>
            <link>https://velog.io/@sopt_official/Nextjs-%ED%86%BA%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@sopt_official/Nextjs-%ED%86%BA%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Wed, 28 Dec 2022 16:57:41 GMT</pubDate>
            <description><![CDATA[<h2 id="✨-nextjs">✨ Nextjs</h2>
<hr>
<div align="center">
<img width=800px src="https://user-images.githubusercontent.com/97586683/199684032-a5fb125c-e29b-4c54-ba7e-dea04ee2f95e.png">
</div>

 <br/>

<hr>
<p>Nextjs 는 React 의 대표적인 프레임워크로 <b>서버서이드 렌더링(Server-Side Rendering, SSR)</b>을 대표적 기능으로 내세우는 것이 특징이다.<br>처음 접하는 사람들은 현재 사용하는 React에서 불편함을 느끼지 못하는 경우도 있고, <em>서버사이드렌더링</em> 이 얼마나 유용한 기능인지 와닿지 않는 사람들도 있을 것이다.<br>따라서 해당 챕터에서는 Nextjs의 대표적인 특징인 SSR 과 이것이 어떤 이득을 주는지와<br>30기 앱잼 <a href="https://www.with-nori.com/">nori</a> 에서 중점적으로 사용하고 공부했던 Next 에서의 라우팅 기능을 소개하고자 한다.</p>
<hr>
<p><br/><br/></p>
<h3 id="✨-server-side-renderingssr">✨ Server-Side Rendering(SSR)</h3>
<hr>
<p>30기 앱잼 초창기때, 29기 앱잼 서비스인 <em>북스테어즈</em> 에서 서비스를 기존 React 에서 Next로 리팩터링 한다는 이야기를 들었다.<br>북스테어즈에서 리팩터링을 하는 이유, 많은 서비스, 블로그에서 기술하듯이 Next를 사용하는 가장 큰 이유는 <b>SEO(Search Engine Optimization)</b>를 위한 <b>Server-Side Rendering(SSR)</b>이 가능하기 때문이라고 한다.<br>이게 무슨 뜻일까? 예시를 통해 보도록 하자.</p>
<br/>

<div align="center">
<img width=650px src="https://user-images.githubusercontent.com/97586683/201011124-57a86b0c-a086-4254-a13a-434488d16d66.png">
</div>

<br/>

<p>기존 React 에서는 <b>Client-Side Rendering(CSR)</b>을 한다.<br>CSR 에서는 위 그림처럼 웹사이트를 요청했을 때 빈 <b>html을 가져온 후 script를 로딩하기 떄문</b>에, 첫 로딩 시간도 오래걸리고 SEO에 취약하다는 단점이 있다.(html 자체는 비어있기 때문에!)<br>또한 네트워크가 연결이 되어있지 않거나 느린 경우에 서비스 이용자는 script가 로딩되기 전까지 빈 화면을 보게 되는 경우가 생기게 된다.</p>
<br/>

<div align="center">
<img width=650px src="https://user-images.githubusercontent.com/97586683/201013028-3f8a3e1f-a62f-4cfc-bcfc-8e5be5a80423.png">
</div>

<br/>

<p>반면 Nextjs 는 <b>pre-reloading</b> 을 통해 미리 데이터가 렌더링된 페이지를 가져올 수 있게 해 준다.<br>이는 사용자에게 더 좋은 경험을 주고 검색 엔진에 잘 노출 될 수 있도록 해주는 SEO에서도 장점을 얻을 수 있다.(HTML이 비어있지 않기 때문에!)</p>
<p><br/><br/><br/></p>
<h2 id="✨-next의-가장-중요한-기능-라우팅">✨ Next의 가장 중요한 기능 라우팅</h2>
<hr>
<p>지난 번 APP-JAM 에서 <a href="https://www.with-nori.com/">nori</a> 서비스를 개발하며 가장 인상깊게 남았던 부분은 
Header는 각각의 페이지로 이동하는, Community는 고유의 내용이 담겨있는 페이지로 이동해야 하는 기능을 구현하는 것과 SSR을 적용시키는 방법이었다. 
기존의 React와 Next에서 제공하는 라우팅이 어떻게 다른지 비교하며 알아보자.</p>
<h3 id="1-nextjs-의-라우팅파일시스템-기반-라우팅--정적라우팅">1. Next.js 의 라우팅(파일시스템 기반 라우팅) / 정적라우팅</h3>
<hr>
<p>기존의 React에서의 라우팅이라 함은 Router를 별도로 설치하여 Router 파일에 컴포넌트를 import 하고 pathname을 지정해주는 방식이다.</p>
<pre><code class="language-Jsx">// 합동세미나 당시 React 로 구현한 Router.
import Home from &#39;pages/Home&#39;;
import Room from &#39;pages/Room&#39;;
import WishList from &#39;pages/WishList&#39;;
import React from &#39;react&#39;;
import Wish from &#39;pages/Wish&#39;;
import ScrollToTop from &#39;./ScrollToTop&#39;;
import { BrowserRouter, Route, Routes } from &#39;react-router-dom&#39;; // 컴포넌트 import

function Router() {
  return (
    &lt;BrowserRouter&gt;
      &lt;ScrollToTop /&gt;
      &lt;Routes&gt;
        &lt;Route path=&quot;/&quot; element={&lt;Home /&gt;} /&gt;
        &lt;Route path=&quot;/wishlist&quot; element={&lt;WishList /&gt;} /&gt;
        &lt;Route path=&quot;/wish/:id&quot; element={&lt;Wish /&gt;} /&gt;
        &lt;Route path=&quot;/wish/room/:id&quot; element={&lt;Room /&gt;} /&gt;
        &lt;Route path=&quot;/*&quot; element={&lt;p&gt;Page Not Found&lt;/p&gt;} /&gt;
      &lt;/Routes&gt;
    &lt;/BrowserRouter&gt;
  );
}</code></pre>
<br/>

<p>하지만 Next.js는 <strong>파일시스템 기반 라우팅</strong> 을 지원한다. 
때문에 더 직관적인 라우팅과 컴포넌트 구분이 가능하다.</p>
<div align="center">
<img width="300" alt="pages폴더" src="https://user-images.githubusercontent.com/97586683/199683093-1ca4c4e1-4569-47e3-a2f5-8e9e80b78902.png" />
</div>

<ul>
<li>최초 프로젝트를 생성하면 자동적으로 <code>pages</code> 라는 폴더가 생성된다.</li>
<li><code>pages</code> 폴더 안에 컴포넌트를 생성 하면 <strong>자동으로 경로가 설정되게 된다.</strong></li>
<li><code>.tsx(혹은 .jsx, js, ts)</code> 파일을 생성 한 후 <code>useRouter</code>나 <code>Link</code> 컴포넌트를 이용하여 해당 파일의 이름으로 경로를 설정한다.</li>
</ul>
<pre><code class="language-Tsx">// /common/Header.tsx 의 Menu 컴포넌트 일부
  &lt;Link href=&quot;/viewProduct&quot;&gt;
    &lt;StMenuBtn type=&quot;button&quot; onClick={handleClickExcept}&gt;
      상품보기
    &lt;/StMenuBtn&gt;
  &lt;/Link&gt;
  &lt;Link href=&quot;/community&quot;&gt;
    &lt;StMenuBtn type=&quot;button&quot; onClick={handleClickExcept}&gt;
      커뮤니티
    &lt;/StMenuBtn&gt;</code></pre>
<div align="center">
<img  width="350" alt="정적 라우팅 path" src="https://user-images.githubusercontent.com/97586683/199683107-86ff01f0-7a30-449d-8499-443f0f64d1d4.png">
</div>

<ul>
<li><code>Link</code> 컴포넌트는 <code>next/Link</code> 모듈에서 불러와 사용 할 수 있으며 <code>href</code> 속성으로 이동할 경로를 지정한다.</li>
<li>위 코드의 <code>StMenuBtn</code> 을 클릭하게 되면 <code>Community</code> 컴포넌트를 렌더링 하게 되고, 경로 또한 자동적으로 파일명인 <code>/Community</code> 가 된다.</li>
<li>이처럼 파일명으로 직관적으로 라우팅을 할 수 있음으로써 컴포넌트의 이름과 path의 이름을 통일하여 더 직관적인 컴포넌트 관리가 가능하다.</li>
<li>또한 <code>Link</code> 컴포넌트는 브라우저의 <em>History API</em>를 지원하여 뒤로가기를 하더라도 이전에 렌더링한 페이지를 불러온다. 따라서 새 페이지를 불러오기 위한 요청을 하지 않아도 되며, 이전 페이지를 다시 컴파일할 필요가 없다는 장점이 있다.</li>
</ul>
<p><br/><br/></p>
<h3 id="2-동적라우팅">2. 동적라우팅</h3>
<hr>
<br/>

<p>Next.js 에서는 지정된 주소로의 정적 라우팅 뿐 만 아니라, 동적인 주소로의 라우팅도 가능한데</p>
<div align="center">
<img width="300" alt="동적라우팅 파일구조" src="https://user-images.githubusercontent.com/97586683/199683117-22fa5369-96bb-470d-a641-f6f2f5ee86f0.png">
</div>

<ul>
<li>동적 라우팅을 사용하기 위해서는 폴더 구조를 조금 수정을 해야 한다.</li>
<li>위 사진의 <code>community</code> 폴더 안의 <code>index.tsx</code> 는 path가 <code>/community</code> 인 파일이다. <strong>이렇게 해당하는 pathname 안에 있는 index 파일은 폴더 이름을 path로 가진다.</strong></li>
<li>다음으로 대괄호 <code>[]</code>로 파일명을 감싸면 해당 페이지는 동적으로 경로가 지정되는 페이지가 된다.</li>
<li>이제 동적 페이지가 존재하는 경로에 임의의 주소를 대입하면 <strong>대입한 주소를 쿼리명으로 갖는 페이지로 이동 할 수 있다.</strong></li>
</ul>
<pre><code class="language-Tsx">  &lt;StMainInfo
    onClick={() =&gt; Router.push({ pathname: `/community/${id}` })}
  &gt;
    &lt;h1&gt;{title}&lt;/h1&gt;
    &lt;p&gt;{content}&lt;/p&gt;
  &lt;/StMainInfo&gt;</code></pre>
<p>위 코드는 <code>{title}</code> 과 <code>{contnet}</code>를 클릭하면 해당 <code>{id}</code>값을 가지는 path로 이동하게 되는 기능을 구현한 코드이다.<br>여기서 <code>{id}</code> 는 API 에서 가져오는 각 게시글의 id 값이다.</p>
<div align="center">
<img width="350" alt="동적라우팅 path" src="https://user-images.githubusercontent.com/97586683/199683125-4e8ff588-f589-471f-a6e3-9269af975ec6.png">
</div>

<br/>

<ul>
<li>동적 라우팅을 사용할떄는 하나의 주의사항이 있는데, 한 동적 경로에는 한 개의 동적 페이지만이 존재 할 수 있다.</li>
</ul>
<pre><code>/.next
/pages
  ㄴ-- index.jsx
  ㄴ-- /community
         ㄴ-- [cid].jsx
         ㄴ-- [name].jsx -&gt; Error!
/public
/styles</code></pre><p><br/><br/></p>
<h2 id="next-v13의-등장">Next v13의 등장</h2>
<img src="https://user-images.githubusercontent.com/79238676/199297677-6a01cce0-8260-4f23-9455-a35b7423ab07.png" width="1000" height="400">

<p>지난 10/26에 Next.js 13버전을 발표했다. </p>
<p>이 글을 통해 어떤 부분이 달라졌고, 내가 배포한 <a href="https://www.with-nori.com/">nori</a> 에서 변경해야 할 부분을 먼저 점검해보고자 한다.<br><a href="https://nextjs.org/blog/next-13">공식문서</a>를 참고하였다. 
<br/></p>
<p>👇 최신 버전을 다운로드 하고자 하면 아래의 명령을 수행하면 된다.</p>
<pre><code class="language-ts">npm i next@latest react@latest react-dom@latest eslint-config-next@latest</code></pre>
<h3 id="✨-app-directory-beta">✨ app/ Directory (beta)</h3>
<p>✨ 라우팅을 비교해보자</p>
<p>Next.js에서 가장 사랑받는 기능 중 하나는 파일 시스템 라우터이다. 즉, 폴더 안에 파일을 옮겨놓기만 애플리케이션에서 즉시 경로를 생성할 수 있다.</p>
<div align="center">
<img src="https://user-images.githubusercontent.com/79238676/199297643-0fba6fd1-d6f1-4ff6-bee3-7519d7beb6b0.png" width="200" height="300">
</div>
<br/>
위의 폴더 구조를 따르게 되면 https://www.with-nori.com/write, https://www.with-nori.com/comunity로 경로가 생성된다.
<div align="center">
<img src="https://user-images.githubusercontent.com/79238676/199297645-0096a2ad-9be9-4f4c-b636-c927c132e294.png" width="200" height="300">
</div>

<p>👇 일단, app/ 의 구조로 바꾸기 위해서는 next.config.js를 수정해야 한다.</p>
<pre><code class="language-ts">/** @type {import(&#39;next&#39;).NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
  // 추가
  experimental: { appDir: true },
};

module.exports = nextConfig;</code></pre>
<hr>
<h3 id="✨-layouts">✨ Layouts</h3>
<p>주요 업데이트 내용을 살펴보자</p>
<p>하지만 이제는 pages가 아닌 app에 page와 layout을 넣어야 한다.</p>
<div align="center">
<img src="https://user-images.githubusercontent.com/79238676/199297522-d6baf1c6-626f-4916-8c12-90bed6f18d76.png" width="350" height="150">
</div>

<pre><code class="language-ts">//app/layout.tsx

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    &lt;html&gt;
      &lt;head&gt;&lt;/head&gt;
      &lt;body&gt;{children}&lt;/body&gt;
    &lt;/html&gt;
  );
}</code></pre>
<pre><code class="language-ts">// app/page.tsx

export default function page() {
  return &lt;div&gt;메인 페이지 입니다&lt;/div&gt;;
}</code></pre>
<p>해당 페이지와 레이아웃을 적용한 결과이다.</p>
<div align="center">
<img src="https://user-images.githubusercontent.com/79238676/199297537-b66307dc-bafb-4919-8868-be53711b0dd8.png" width="400" height="180">
</div>

<p>여기서 RootLayout에 네비게이션바를 넣어주면..!</p>
<pre><code class="language-ts">export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    &lt;html&gt;
      &lt;head&gt;&lt;/head&gt;
      &lt;body&gt;
        &lt;nav&gt;네비게이션 바&lt;/nav&gt;
        {children}
      &lt;/body&gt;
    &lt;/html&gt;
  );
}</code></pre>
<p>아래처럼 네비게이션바가 defalut로 설정된 것을 확인할 수 있다💡</p>
<div align="center">
<img src="https://user-images.githubusercontent.com/79238676/199297548-3343500b-f26c-4770-a1fc-a4659d769cbd.png" width="400" height="180">
</div>

<h3 id="✨-data-fetching">✨ Data Fetching</h3>
<div align="center">
<img src="https://user-images.githubusercontent.com/79238676/199297562-3fad7a8d-b6b8-4f08-bc62-07cecffa4096.png" width="550" height="300">
</div>

<p>지금까지 next에서 SSR을 적용할 때는 getServerSideProps, getStaticProps를 사용하였다.</p>
<pre><code class="language-ts">import { GetServerSideProps } from &quot;next&quot;;

export default function page() {
  return &lt;div&gt;메인 페이지 입니다&lt;/div&gt;;
}
export const getServerSideProps: GetServerSideProps = async (context) =&gt; {
  const res = await fetch(&quot;https://dummyjson.com/todos/1&quot;);
  const data = await res.json();

  return {
    props: {
      data,
    },
  };
};</code></pre>
<p>하지만, app에서는 해당 메소드를 지원하지 않는다는 것을 확인할 수 있다.</p>
<div align="center">
<img src="https://user-images.githubusercontent.com/79238676/199297569-d4dcf636-92ac-4e83-b84a-1247f261fd75.png" width="700" height="200">
</div>

<p>그렇다면 어떻게 데이터를 불러와야 할까?</p>
<pre><code class="language-ts">import { use } from &quot;react&quot;;

export default function page() {
  const data = use(getData());
  return (
    &lt;div&gt;
      메인 페이지 입니다&lt;p&gt;{data.todo}&lt;/p&gt;
    &lt;/div&gt;
  );
}
export const getData = async () =&gt; {
  const res = await fetch(&quot;https://dummyjson.com/todos/1&quot;);
  const data = await res.json();

  return data;
};</code></pre>
<p>위와 같이 getData라는 함수를 선언한 뒤에, use() 안에 넣어주기만 하면 요청이 이루어진다.</p>
<br/>

<p>❗️❗️❗️여기서❗️❗️❗️</p>
<p>fetch 구문의 URL 뒤에 { cache: &#39;&#39; } 자리에 무엇이 들어가냐에 따라 기존의 getServerSideProps, getStaticProps와 유사한 기능을 구현하게 된다.</p>
<div align="center">
<img src="https://user-images.githubusercontent.com/79238676/199297586-d4a6df56-9a7f-4d68-ae6c-5cedd9a55c5d.png" width="400" height="180">
</div>
성공적으로 Data Fetching이 이루어진 것을 확인할 수 있다.

<h3 id="✨-nextimage">✨ next/Image</h3>
<p>👇Image 컴포넌트를 통해서 이미지 파일을 넣을 수 있는데,</p>
<pre><code class="language-ts"> &lt;Image
          src={`https://nori-image.s3.ap-northeast-2.amazonaws.com/${image}`}
        &gt;&lt;/Image&gt;</code></pre>
<p>Next.js 13에서는 아래의 두 가지가 업데이트되었다.</p>
<ul>
<li>alt기본적으로 태그를 필요로 하는 접근성 향상</li>
<li>이미지 로드가 느릴 경우에 기존의 레이아웃이 밀려나는 현상인 Layout Shift를 막기위해 자동으로 width height를 설정하여 최적화</li>
</ul>
<h3 id="✨-nextfont">✨ next/font</h3>
<p>👇font도 Image와 마찬가지로 Layout Shift를 막고, 이제 구글 폰트가 내장되어진것이 크게 달라졌다!</p>
<pre><code class="language-ts">yarn add @next/font</code></pre>
<p>폰트를 설치하고, 원하는 구글 폰트를 불러와 className으로 적용시키면 된다.</p>
<pre><code class="language-ts">import { Roboto_Mono } from &quot;@next/font/google&quot;;

const roboto = Roboto_Mono({
  weight: &quot;400&quot;,
});
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    &lt;html className={roboto.className}&gt;
      &lt;head&gt;&lt;/head&gt;
      &lt;body&gt;
        &lt;nav&gt;네비게이션 바&lt;/nav&gt;
        {children}
      &lt;/body&gt;
    &lt;/html&gt;
  );
}</code></pre>
<p>아래처럼 글꼴이 바뀌어 진 것을 확인할 수 있다.</p>
<div align="center">
<img src="https://user-images.githubusercontent.com/79238676/199297590-24637e3b-1ba6-4bac-8e39-13f60c0a5e18.png" width="400" height="180">
</div>

<h3 id="✨-nextlink">✨ next/link</h3>
<p>기존 Next.js 12에서는 태그 이후에 자식으로 반드시 태그를 필요로 하였다.</p>
<pre><code class="language-ts">&lt;Link href={`/write/${cid}`}&gt;
    &lt;li&gt;
        &lt;a&gt;수정하기&lt;/a&gt;
    &lt;/li&gt;
&lt;/Link&gt;</code></pre>
<p>하지만, Next.js 13에서는 항상 를 렌더링 하기에 자식으로 태그를 가질 필요가 없어졌다.</p>
<pre><code class="language-ts">&lt;Link href={`/write/${cid}`}&gt;
    &lt;li&gt;수정하기&lt;/li&gt;
&lt;/Link&gt;</code></pre>
<h1 id="글을-줄이며">글을 줄이며...</h1>
<blockquote>
<p>지난 앱잼에서 Next.js 를 사용할때 가장 중요하고 인상깊었던 기능인 라우팅 방법에 대해 다시 한 번 살펴보았다.
더불어, 최근 Next.js 는 13버전을 발표하면서 새로운 기능들이 추가되었다. </p>
</blockquote>
<p>지난 앱잼에서 Next.js 를 사용할때 가장 중요하고 인상깊었던 기능인 라우팅 방법에 대해 다시 한 번 살펴보았다.
더불어, 최근 Next.js 는 13버전을 발표하면서 라우팅 방식의 변경 뿐 만 아니라 새로운 기능들이 추가되었다. </p>
<p>물론, react-router-dom의 업데이트 만큼 많은 부분이 바뀐 것은 아니었지만, Link태그나 이미지와 app/으로 페이지 이동을 구현하는 것은 재미있게 살펴보았다. </p>
<p>지금까지 살펴본 Next.js로 어떻게 해야 효과적으로 SSR을 적용시키면서 서비스를 운영해야 할 지에 대해서 꾸준한 고민과 공부가 필요하다고 느껴지는 시간이었다. </p>
<br/>

<hr>
<p><strong>작성자</strong></p>
<p><a href="https://github.com/Happhee">IN SOPT WEB, OB 홍서희</a>
<a href="https://github.com/Brokyeom">IN SOPT WEB, OB 김형겸</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Compositional Layout에 대하여]]></title>
            <link>https://velog.io/@sopt_official/iOS1</link>
            <guid>https://velog.io/@sopt_official/iOS1</guid>
            <pubDate>Wed, 28 Dec 2022 07:40:02 GMT</pubDate>
            <description><![CDATA[<p>세미나, 합동 세미나, 스터디…… SOPT에서 많은 활동을 하면서 이전과는 비교도 안 되는 복잡한 뷰들을 많이 만들어 봤던 것 같아요.</p>
<img width="837" alt="스크린샷 2022-12-23 오후 12 04 00" src="https://user-images.githubusercontent.com/108191001/209262588-6a69b25d-c092-495c-83a4-838e94a6f210.png">

<p>그 중 하나가 이 뷰였는데요. 여러분들은 이 뷰를 어떻게 구현하시나요? 저는 전체 tableView를 만들고, 각 tableViewCell들에 collectionView를 넣는 방법을 사용했어요. 혹은 첫 번째와 세 번째는 header, footer로 구현하면 그래도 고생은 덜하지 않을까? 이런 고민을 했었던 기억이 나요. </p>
<p>열심히 tableViewCell을 만들고, 그 안에 다시 collectionView를 넣던 와중에 문득 이런 의문이 들었어요. 아니 왜 이렇게 비효율적인 짓을 하지… 요즘 시대에 뭐라도 만들어 놨어야 하는 거 아닌가… 라는 생각에 검색을 해 보니 아니나 다를까 이미 <code>Compositional Layout</code> 이라는 게 나와 있더라구요?</p>
<p>(단, iOS 13 이상만 지원 💦)</p>
<p>어쨌든 제가 당장 공부해 봤습니다 ~ 😎</p>
</br>
</br>

<h3 id="compositional-layout의-등장">Compositional Layout의 등장</h3>
<img width="837" alt="스크린샷 2022-12-23 오후 12 06 08" src="https://user-images.githubusercontent.com/108191001/209262796-667f5524-0189-4b16-82ff-20cd5a731816.png">

<p>당장 앱스토어만 봐도 요즘 앱의 디자인들이 얼마나 복잡한지 알 수 있어요. 앱스토어를 tableView와 collectionView가 중첩되는 뷰로 구현해야 한다고 생각하면 정말 머리가 아픕니다. 이를 보완하기 위해 flexible하고 빠르게 어떤 레이아웃이든 만들 수 있는 <code>compositional layout</code>이 탄생하게 됩니다.</p>
</br>

<h3 id="구성">구성</h3>
<img width="728" alt="스크린샷 2022-12-23 오후 12 07 16" src="https://user-images.githubusercontent.com/108191001/209262902-c622692b-f528-4df0-98a2-bb7b4eefa98b.png">

<p>Compositional Layout은 item, group, section, layout으로 구성됩니다. 하나의 layout에 section, section 안에 group, group 안에 item들이 있어요. 각 요소들의 size를 정하고 지정해 주기만 하면 레이아웃이 완성돼요.</p>
</br>

<h3 id="크기">크기</h3>
<img width="728" alt="스크린샷 2022-12-23 오후 12 08 07" src="https://user-images.githubusercontent.com/108191001/209262973-aa170be7-707b-4dd4-9e90-8fbb7ea41a6c.png">

<p>각 요소들의 size를 정해 주는 방법은 3가지가 존재합니다.</p>
<ul>
<li><code>fractionalWidth</code> &amp; <code>fractionalHeight</code> - 컨테이너와의 너비&amp;높이 비율</li>
<li><code>absolute</code> - 포인트값으로 지정</li>
<li><code>estimated</code> - 후에 content의 크기가 바뀌어 크기가 정확하지 않을 때는 estimate 값으로 </li>
</ul>
</br>

<pre><code class="language-swift">let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.2),
                                     heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)</code></pre>
<p>item은 group 안에 존재해요. 따라서 item의 size는 group과의 비율로 나타내게 됩니다. 그렇다면 이 코드에서 item의 너비는 group 너비의 20%, 높이는 group 높이의 100%가 되겠죠. 그리고 이러한 size로 item을 구성하겠다고 지정해 주기만 하면 됩니다. group과 section 모두 이런 식으로 구성하면 됩니다.</p>
</br>

<h3 id="item">item</h3>
<img width="728" alt="스크린샷 2022-12-23 오후 12 08 53" src="https://user-images.githubusercontent.com/108191001/209263058-1e5290cd-ee53-4152-b41f-bba21a75dfe0.png">

</br>

<h3 id="group">Group</h3>
<img width="728" alt="스크린샷 2022-12-23 오후 12 09 06" src="https://user-images.githubusercontent.com/108191001/209263080-b56b16a8-1488-4407-a1e8-31d65dfb22b2.png">

<p>지정된 방식에 따라 item들을 배치합니다. 3가지 방식이 존재합니다.</p>
<ul>
<li>horizontal</li>
<li>vertical</li>
<li>custom ⇒ custom하여 아이템의 absolute 크기와 위치 지정 가능</li>
</ul>
</br>

<h3 id="section">Section</h3>
<img width="728" alt="스크린샷 2022-12-23 오후 12 09 19" src="https://user-images.githubusercontent.com/108191001/209263126-a99c0e9c-1f96-45b5-afab-d1647ec98dc8.png">

</br>

<h3 id="layout">Layout</h3>
<img width="728" alt="스크린샷 2022-12-23 오후 12 10 38" src="https://user-images.githubusercontent.com/108191001/209263442-7c2d0948-1ed5-4141-8398-02545dfec271.png">

<ul>
<li>iOS, tvOS - <code>UICollectionViewCompositionalLayout</code></li>
<li>macOS - <code>NSCollectionViewCompositionalLayout</code></li>
<li>플랫폼에 따라 이름은 다르지만 정의는 동일합니다.</li>
<li><strong><code>provider</code> 클로저로 섹션마다 다양한 레이아웃을 정의할 수 있습니다.</strong> ⇒ 이제 섹션별 레이아웃이 완전히 구별될 수 있기 때문에 많은 가능성이 열립니다.</li>
</ul>
</br>

<p>예제 2개를 살펴보고 마무리 하겠습니다.</p>
<h3 id="example---grid">Example - Grid</h3>
<p><code>Compositional Layout</code>을 사용하여 Grid 형태를 구현해 볼게요.</p>
<img width="728" alt="스크린샷 2022-12-23 오후 12 12 06" src="https://user-images.githubusercontent.com/108191001/209263587-1cbf1144-18c6-4095-8961-5428f6c3e389.png">

<pre><code class="language-swift">    private func createLayout() -&gt; UICollectionViewLayout {
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.2),
                                              heightDimension: .fractionalHeight(1.0))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        item.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)

        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                               heightDimension: .fractionalWidth(0.2))
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])

        let section = NSCollectionLayoutSection(group: group)

        let layout = UICollectionViewCompositionalLayout(section: section)

        return layout
    }</code></pre>
<ol>
<li>item은 group 안에 존재합니다. 
따라서 item의 너비 = container(group) 너비의 20%
item의 높이 = container(group) 높이와 동일(100%)</li>
</ol>
<ol start="2">
<li>group은 section 안에 존재합니다.
group의 너비 = container(section) 너비와 동일(100%)
group의 높이 = container(section) 너비의 20%(item이 정사각형이 됨)</li>
</ol>
<ol start="3">
<li>item에 Inset도 추가해서 구현해 보았습니다.</li>
</ol>
</br>

<h3 id="example---여러-개의-section">Example - 여러 개의 Section</h3>
<img width="829" alt="스크린샷 2022-12-23 오후 12 14 38" src="https://user-images.githubusercontent.com/108191001/209263842-8705f757-6336-47ab-b237-810828067366.png">

<p>여러 개의 section을 가진 뷰를 만들어 볼게요.</p>
<pre><code class="language-swift">    enum SectionLayoutKind: Int, CaseIterable {
        case list, grid1, grid2

        var columnInt: Int {
            switch self {
            case .list:
                return 1
            case .grid1:
                return 5
            case .grid2:
                return 2
            }
        }
    }</code></pre>
<p>섹션이 여러 개이므로, enum으로 만들어 관리해 줍니다. <code>columnInt</code>는 각 section에 나타낼 열의 개수를 표시합니다. list 섹션의 column은 1이 되고, grid1의 열은 5개, grid5의 열은 2개가 되도록 했습니다.</p>
<pre><code class="language-swift"> private func createLayout() -&gt; UICollectionViewLayout {
        let layout = UICollectionViewCompositionalLayout { (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -&gt; NSCollectionLayoutSection? in

            guard let sectionLayoutKind = SectionLayoutKind(rawValue: sectionIndex) else {return nil}
//            print(sectionLayoutKind)
            let columns = sectionLayoutKind.columnInt
//            print(columns)

            var itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                                  heightDimension: .fractionalHeight(1.0))
            if columns == 5 {
                itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.2),
                                                  heightDimension: .fractionalHeight(1.0))
            } else if columns == 2 {
                itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5),
                                                  heightDimension: .fractionalHeight(1.0))
            }


            let item = NSCollectionLayoutItem(layoutSize: itemSize)
            item.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)

            let groupHeight = columns == 1 ?
            NSCollectionLayoutDimension.absolute(44) :
            NSCollectionLayoutDimension.fractionalWidth(0.2)

            let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                                   heightDimension: groupHeight)
            let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, repeatingSubitem: item, count: columns)

            let section = NSCollectionLayoutSection(group: group)

            return section
        }

        return layout

    }</code></pre>
<ol>
<li><code>provider</code> 클로저로 섹션마다 다양한 레이아웃을 정의할 수 있다고 앞서 언급했었는데요. 이번 예제 같은 여러 개의 섹션을 구현할 때 사용됩니다.</li>
</ol>
<pre><code class="language-swift">init(sectionProvider: UICollectionViewCompositionalLayoutSectionProvider)</code></pre>
<ol start="2">
<li>item
list 형태일 때는 group의 너비, 높이와 동일하게 지정합니다.
grid1일 때는, 열이 5개가 되도록 너비가 group 너비의 20%가 되도록 합니다.
grid2일 때는, 열이 2개가 되도록 너비가 group 너비의 50%가 되도록 합니다.
마찬가지로 item에 inset을 주었습니다.</li>
</ol>
<ol start="3">
<li>결과 이미지를 보면 각 section의 group마다 높이가 다릅니다. 이를 고려하여 <code>groupHeight</code> 상수를 만들어 열이 1개일 시와 아닐 때로 나누어 size를 지정해 주었습니다. </li>
</ol>
</br>
</br>


<p>이런 식으로 레이아웃이 완전히 바뀌어도 레이아웃을 정의하는 코드 자체는 크게 변화하지 않는다는 게 정말 흥미로운 점 같아요. 
WWDC를 보면 더 많은 예제들과 DataSource를 사용하는 새로운 방법에 대한 설명도 있어요. 
관련 링크를 두고 이만 글을 마치겠습니다. 👋🏻</p>
</br>

<p><a href="https://developer.apple.com/videos/play/wwdc2019/215/">💻 WWDC 19 - Advances in Collection View Layout</a>
<a href="https://developer.apple.com/videos/play/wwdc2019/220">💻 WWDC 19 - Advances in UI Data Sources</a>
<a href="https://developer.apple.com/videos/play/wwdc2020/10097">💻 WWDC 20 - Advances in UICollectionView</a></p>
<p><strong>작성자</strong>
<a href="https://github.com/minnnidev">IN SOPT, YB iOS 김민</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[WorkManager를 소개합니다 ✨]]></title>
            <link>https://velog.io/@sopt_official/Introduction-to-WorkManager</link>
            <guid>https://velog.io/@sopt_official/Introduction-to-WorkManager</guid>
            <pubDate>Wed, 07 Dec 2022 04:19:36 GMT</pubDate>
            <description><![CDATA[<h2 id="motivation">Motivation</h2>
<blockquote>
<p>Asynctask was intended to enable proper and easy use of the UI Thread.
AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the <code>java.util.concurrent</code> package such as <code>Executor</code>, <code>ThreadPoolExecutor</code> and <code>FutureTask</code>.</p>
</blockquote>
<p>AsyncTask는 짧은 시간에 수행되는 태스크들을 백그라운드에서 돌려야할 때 사용되었다. 비록 이제는 Deprecated 되어서 RxJava, Coroutine으로 대체되었지만.</p>
<p>그렇다면 백그라운드에서 장시간 태스크를 수행해야한다면 어떤 것을 활용해야할까?</p>
<h2 id="before-workmanager">Before WorkManager</h2>
<h3 id="buildsdk_int--19-kitkat">Build.SDK_INT &lt; 19 (KitKat)</h3>
<p>AlarmManager와 BroadcastReceiver를 활용하여 만든다. </p>
<pre><code class="language-java">public class SchedulerSetupReceiver extends BroadcastReceiver {
    private static final String APP_TAG = &quot;siba.android&quot;;

    private static final int EXEC_INTERVAL = 20 * 1000;

    @Override
    public void onReceive(final Context context, final Intent intent) {
        Log.d(APP_TAG, &quot;SchedulerSetupReceiver.onReceive() called&quot;);
        AlarmManager alarmManager = (AlarmManager) context
                .getSystemService(Context.ALARM_SERVICE);
        Intent receiverIntent = new Intent(context, SchedulerEventReceiver.class);
        PendingIntent intentExecuted = PendingIntent.getBroadcast(context, 0, receiverIntent,
                PendingIntent.FLAG_CANCEL_CURRENT);
        Calendar now = Calendar.getInstance();
        now.add(Calendar.SECOND, 20);
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
                now.getTimeInMillis(), EXEC_INTERVAL, intentExecuted);
    }
}</code></pre>
<p>AlarmManager: 특정 시간에 어떤 코드를 수행하기 위해서 작성된 코드 위의 동작은 20초마다 특정 작업을 수행하기 위해서 <code>alarmManager.setRepeating</code> 함수를 사용한다.</p>
<p>하지만 API 19에서 AlarmManager는 정확히 특정 시간에 알람이 오지 않고 특정 시간에 알람이 몰아서 오거나 지연 처리 등의 이슈가 있어서 백그라운드 태스크를 작업하는데 애로사항이 존재했었다.</p>
<h3 id="buildsdk_int--21-lolipop">Build.SDK_INT &gt;= 21 (Lolipop)</h3>
<p>이런 부정확성을 해결하기 위해서 안드로이드에서는 JobScheduler라는 새로운 백그라운드 태스크 처리 API를 내놓았다.</p>
<p>기존 알람매니저는 특정 주기마다 핸드폰이 어떤 상황이든 관계 없이 동작시켰다면, 잡스케줄러는 특정상황(디바이스가 충전 중이고 핸드폰을 만지지 않는 상황(IDLE))에서만 백그라운드 태스크를 작업할 수 있게 만들었고 비주기적인(1회성) 백그라운드 태스크도 작동할 수 있게 만들었다.</p>
<pre><code class="language-kotlin">class MyJobService : JobService() {
    companion object {
        private val TAG = &quot;MyJobService&quot;
    }

    override
    fun onStartJob(params: JobParameters): Boolean {
        Log.d(TAG, &quot;onStartJob: ${params.jobId}&quot;)

        thread(start = true) {
            Thread.sleep(1000)
            Log.d(TAG, &quot;doing Job in other thread&quot;)
            jobFinished(params, false)
        }

        return true
    }

    override fun onStopJob(params: JobParameters): Boolean {
        Log.d(TAG, &quot;onStopJob: ${params.jobId}&quot;)
        return false
    }
}


        btnJob1.setOnClickListener {
            val js = getSystemService&lt;JobScheduler&gt;(Context.JOB_SCHEDULER_SERVICE)
            val serviceComponent = ComponentName(this, MyJobService::class.java)
            val jobInfo = JobInfo.Builder(JOB_ID_A, serviceComponent)
                // 최소한 이때는 작업이 수행된다
                .setMinimumLatency(TimeUnit.MINUTES.toMillis(1))
                .setOverrideDeadline(TimeUnit.MINUTES.toMillis(3))
                .build()
            js.schedule(jobInfo)
            Log.d(TAG, &quot;Scheduled JobA&quot;)
        }

        btnJob2.setOnClickListener {
            val js = getSystemService&lt;JobScheduler&gt;(Context.JOB_SCHEDULER_SERVICE)
            val serviceComponent = ComponentName(this, MyJobService::class.java)
            val jobInfo = JobInfo.Builder(JOB_ID_B, serviceComponent)
                // 사용자가 폰을 사용하지 않아야됨
                .setRequiresDeviceIdle(true)
                // 반드시 충전중이어야됨
                .setRequiresCharging(true)
                .setPeriodic(TimeUnit.MINUTES.toMillis(15))
                // 주기적으로 해야됨
                .build()
            js.schedule(jobInfo)
            Log.d(TAG, &quot;Scheduled JobB&quot;)
        }</code></pre>
<p>제약조건들</p>
<ul>
<li>setRequiresDeviceIdle</li>
<li>setRequiresCharging</li>
<li>setRequiredNetworkType</li>
<li>setRequiredNetwork</li>
<li>setRequiresBatteryNotLow</li>
<li>setRequiresStorageNotLow</li>
</ul>
<h3 id="api-26의-비극">API 26의 비극</h3>
<p>Background Service의 제약 - App 이 Background 일 때 Foreground Service 가 아니면 Background Service 사용 불가, 백그라운드 서비스를 동시에 여러 앱이 이용하면서 메모리, 배터리 리소스를 마음대로 사용하여 폰 성능을 굉장히 저하시켜 왔었음</p>
<p>구글에서 이런 문제를 해결하기 위해 Google Services에서 Firebase JobDispatcher라는 것을 새로 만들어서 제공, but 이는 그다지 좋은 해결책이 되지 못함(Google Service, 써드파티에 의존성이 생김)</p>
<h2 id="workmanager-resolve-this">WorkManager Resolve This</h2>
<p>결국에는 개발자는 API 21 미만이면 AlarmManager + BroadcastReceiver 조합을, Firebase 의존성이 있다면 Firebase JobDispatcher를 아니면 JobScheduler로 구현된 코드를 모두 가지고 있어야 했습니다. 그러나 WorkManager는 이런 백그라운드 프로세스를 내부에 처리하고 그 과정을 추상화함으로써 개발자로 하여금 한 API에서만 처리할 수 있게 도와줍니다.</p>
<p><img src="https://miro.medium.com/max/1400/1*hpsHT-TCsO3px3Q72-7Nvg.png" alt="img"></p>
<p><img src="https://miro.medium.com/max/1400/1*9WbyMPcUcMq65UDuoDdktg.png" alt="img"></p>
<p>(여기서 JobScheduler에 위치한 포지션을 대체했다고 생각하면 됨)</p>
<p>즉 WorkManager는 실행 보장이 가능하고, 장치 상태에 따라서 태스크를 중단/재실행이 가능한 작업 관리자 역할을 한다. 또한 특정 작업이 완료되면 다른 작업을 진행할 수 있거나 여러 작업들을 동시에 처리할 수 있다(병렬처리).</p>
<p><img src="https://miro.medium.com/max/1400/1*gD2eOZthfr2d5dqZLdHZqw.png" alt="img"></p>
<h2 id="how-to-use-workmanager">How to use WorkManager</h2>
<h3 id="workmanager의-구성">WorkManager의 구성</h3>
<ul>
<li>WorkManager: WorkQueue에 작업들을 넣고 처리한다. 자바에서 BlockingQueue 같은 느낌으로다가</li>
<li>Worker: 이 추상클래스 내부에 doWork라는 함수를 오버라이드 해서 어떤 작업을 할 것인지, 작업의 결과물을 어떻게 배출할 것인지 결정</li>
<li>WorkRequest: Worker의 작업 여부(반복/비주기, 제약사항, 실행조건 등)를 결정하는 클래스.<ul>
<li>OneTimeWorkRequest</li>
<li>PeriodicWorkRequest</li>
</ul>
</li>
</ul>
<pre><code class="language-kotlin">class SimpleWorker : Worker() {
    override fun doWork(): Result {
        Log.d(&quot;Sample&quot;, &quot;SimpleWorker Working...&quot;)
        return Result.SUCCESS
    }
}

    // 작업을 수행하는 클래스 내부
    private fun doWorkOneTime() {
        val workRequest = OneTimeWorkRequestBuilder&lt;SimpleWorker&gt;().build()
        val workManager = WorkManager.getInstance()
        workManager?.enqueue(workRequest)
    }

    private fun doWorkPeriodic() {
        val workRequest = PeriodicWorkRequestBuilder&lt;SimpleWorker&gt;(15, TimeUnit.MINUTES).build()
        PeriodicWorkRequest.Builder(SimpleWorker::class.java, 15, TimeUnit.MINUTES).build()
        val workManager = WorkManager.getInstance()
        workManager?.enqueue(workRequest)
    }

    private fun doWorkWithConstraints() {
        // 네트워크 연결 상태 와 충전 중 인 상태를 제약조건으로 추가 한다
        val constraints = Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .setRequiresCharging(true)
                .build()

        // 제약 조건과 함께 작업 요청 생성
        val requestConstraint  = OneTimeWorkRequestBuilder&lt;SimpleWorker&gt;()
                .setConstraints(constraints)
                .build()

        val workManager = WorkManager.getInstance()

        workManager?.enqueue(requestConstraint)
    }

    private fun doWorkChaining() {
        val compressWork = OneTimeWorkRequestBuilder&lt;CompressWorker&gt;().build()
        val uploadWork = OneTimeWorkRequestBuilder&lt;UploadWorker&gt;().build()

        WorkManager.getInstance()?.apply {
            beginWith(compressWork).then(uploadWork).enqueue()
        }
    }

    private fun doWorkChaining2() {
        // 세개의 필터적용 작업 요청 생성
        val filterWork1 = OneTimeWorkRequestBuilder&lt;FilterWorker&gt;().build()
        val filterWork2 = OneTimeWorkRequestBuilder&lt;FilterWorker&gt;().build()
        val filterWork3 = OneTimeWorkRequestBuilder&lt;FilterWorker&gt;().build()

        val compressWork = OneTimeWorkRequestBuilder&lt;CompressWorker&gt;().build()
        val uploadWork = OneTimeWorkRequestBuilder&lt;UploadWorker&gt;().build()

        WorkManager.getInstance()?.apply {
            beginWith(filterWork1, filterWork2, filterWork3).then(compressWork).then(uploadWork).enqueue()
        }
    }</code></pre>
<h3 id="여러-worker에서-오는-결과물을-처리해야할-때-inputmerger">여러 Worker에서 오는 결과물을 처리해야할 때? InputMerger</h3>
<p><img src="https://miro.medium.com/max/1400/1*UOT1_rHQndtKNBonXdL_mA.png" alt="img"></p>
<p>책의 단어 빈도를 카운트하고 이를 빈도순대로 정렬하는 작업을 한다고(이걸 왜 핸드폰으로 하지) 가정을 해보자. 그렇다면 책1, 책2, 책3에서 카운트 한 Work의 결과물들이 다음 Worker의 <code>inputData</code> 로 올텐데 이런 경우 WorkManager에서 어떻게 처리할까?</p>
<pre><code class="language-kotlin">private fun doWorkChaining() {
        // 전달할 정보를 담은 Map 객체를 생성합니다.
        val input = mapOf(&quot;file_name&quot; to &quot;sdcard/user_choice_picture.jpg&quot;)

        // Data 클래스의 Builder 를 사용해서 input 을 담고 있는 Data 객체를 생성합니다.
        val inputData = Data.Builder().putAll(input).build()

        // 코틀린의 경우 Map 에 제공되는 인라인 함수 toWorkData() 를 이용해 쉽게 Data 객체를 생성할수도 있습니다.
        val inputData = input.toWorkData()

        // WorkRequest 의 setInputData() 메서드로 작업자에 정보를 전달합니다.
        val compressWork = OneTimeWorkRequestBuilder&lt;CompressWorker&gt;()
                .setInputData(inputData)
                .build()

        val uploadWork = OneTimeWorkRequestBuilder&lt;UploadWorker&gt;().build()

        WorkManager.getInstance()?.apply {
            beginWith(compressWork).then(uploadWork).enqueue()
        }
}

class CompressWorker : Worker() {
    override fun doWork(): Result {
        // Data로 들어온 것
        val fileName = inputData.getString(&quot;file_name&quot;, &quot;&quot;)

        Log.d(&quot;Sample&quot;, &quot;CompressWorker Target : ${fileName!!}&quot;)

        // 전달받은 파일이름의 이미지를 압축합니다.

        // 결과물을 compress.jpg 로 저장합니다.

        // 이제 compress.jpg 를 UploadWorker 로 전달합니다.
        outputData = mapOf(&quot;compress_file_name&quot; to &quot;compress.jpg&quot;).toWorkData()

        return Result.SUCCESS
    }
}


class UploadWorker : Worker() {
    override fun doWork(): Result {
        val fileName = inputData.getString(&quot;compress_file_name&quot;, &quot;&quot;)

        Log.d(&quot;Sample&quot;, &quot;UploadWorker Target : ${fileName!!}&quot;)

        return Result.SUCCESS
    }
}</code></pre>
<p>(우선 위와 같이 Data를 Worker들기리 전달을 한다고 한다)</p>
<p><img src="https://miro.medium.com/max/1400/1*HNC_lxA4SWob02V9nmpDHA.png" alt="img"></p>
<p>OverwritingInputMerger 는 여러개의 Data 가 전달될때 같은 key 를 가지는 value 는 덮어쓴다(Overwrite) 데이터 간에 중복 되지 않는 key 의 value 는 새롭게 추가하여 하나의 Data 객체를 만든다.</p>
<p><img src="https://miro.medium.com/max/1400/1*pcvuPKt2sDetpaIP-sbvVA.png" alt="img"></p>
<p>ArrayCreatingInputMerger 는 여러개의 Data 가 전달될때 같은 key를 가지는 value를 배열로 전달합니다.</p>
<pre><code class="language-kotlin">val sortWordWorker = OneTimeWorkRequestBuilder&lt;SortWorker&gt;()
                .setInputMerger(ArrayCreatingInputMerger::class)
                .build()</code></pre>
<h3 id="더-많은-정보를-보고-싶다면">더 많은 정보를 보고 싶다면?</h3>
<p>제가 공유한 더 많은 정보를 보고 싶으시다면 아래 링크를 참고해주세요!</p>
<p><a href="https://velog.io/@l2hyunwoo">이현우의 Velog</a>
<a href="https://medium.com/@l2hyunwoo">이현우의 Medium</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹 접근성에 대한 “내” 생각]]></title>
            <link>https://velog.io/@sopt_official/%EC%9B%B9-%EC%A0%91%EA%B7%BC%EC%84%B1%EC%97%90-%EB%8C%80%ED%95%9C-%EB%82%B4-%EC%83%9D%EA%B0%81</link>
            <guid>https://velog.io/@sopt_official/%EC%9B%B9-%EC%A0%91%EA%B7%BC%EC%84%B1%EC%97%90-%EB%8C%80%ED%95%9C-%EB%82%B4-%EC%83%9D%EA%B0%81</guid>
            <pubDate>Wed, 12 Oct 2022 01:01:25 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/hayounsong/post/2926fec8-be94-4385-96a0-99a529a7c8c4/image.png" alt=""></p>
<p>아무리 빠른 우사인 볼트라도, 다리가 없으면 달릴 수 없다. </p>
<p>아무리 똑똑한 아인슈타인이여도 그 똑똑함을 표현할 수 있는 말이나 행동이 없다면 의미가 없다.</p>
<p>프로그램에서도 마찬가지이다, 
아무리 아름답고 뛰어난 디자인을 가진 웹 사이트라도, 
아무리 신기하고 독특한 기능을 가진 웹 앱이여도, 
아무리 신박한 아이디어를 가진 웹앱이여도, </p>
<p>사용자가 사용할 수 없다면, 결국에는 의미가 없다. </p>
<p>그렇기에, 웹과 사용자의 상호작용은 필수적이며, 
이 과정에서 사용자는 누구나 문제나 물리적•정신적 어려움 없이 웹에 접근하여 사용할 수 있어야 한다. </p>
<p>이것이 바로 &#39;접근성&#39;이다.</p>
<blockquote>
<p><strong>- 접근성은 무엇인가?</strong> 
<strong>- 접근성을 위한 개발은 어떤 것들이 있을까?</strong> 
<strong>- 접근성을 위한 개발을 꼭 해야 할까?</strong></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/hayounsong/post/faf81675-f06e-4895-8aa0-a40e226aad96/image.png" alt=""></p>
<br>
<br>

<h2 id="1-웹-접근성이란">1. 웹 접근성이란?</h2>
<p>키오스크를 예를 들어보자. </p>
<p>키오스크는 정말 편리하다. </p>
<p><strong>의도자체는</strong> 주문에 어려움을 겪어왔던 노인층을 비롯해 어린 아이들까지 사용법을 안다면 누구나 편리하게 사용할 수 있는 시스템이다.</p>
<p>그러나, 이런 키오스크에는 한가지 모순이 있다. </p>
<p>&#39;나이/성별/신분/건강 등등 여러가지 신체적, 환경적 조건에 관계 없이 사용하자!&#39; 
라는 의도로 만들어진 키오스크는,</p>
<p><img src="https://velog.velcdn.com/images/hayounsong/post/0ec4305f-2cc0-47ee-b90e-6c0d7c0094e9/image.png" alt=""></p>
<p>수많은 이들이 사용하는데 어려움을 겪는 하나의 &#39;벽&#39;이 되어버렸다. </p>
<p><strong>한국소비자원</strong>에 따르면, 70세 이상 노인 5명 중 3명은 버스터미널에 설치된 키오스크에서 표를 구입하지 못했고 패스트푸드점 키오스크에서는 5명 모두 주문을 완료하지 못한 것으로 나타났다.</p>
<p>정작 편리하라고 만든 키오스크는, 누군가에겐 어려움이 되어 있었다. </p>
<p>키오스크라는 개체의 아이디어 및 설계 방향은 누구나 편하게 주문을 할 수 있을 것 처럼 보였지만, </p>
<p>정작 그렇지 않았다.</p>
<p>또한, 키오스크에는 재미난 최근 재미난 사건이 하나 있었다. </p>
<p>2022년 초, KT 인터넷망이 하루동안 끊겼던 날. 전국에 있던 수많은 키오스크 매장들은 어떻게 되었을까</p>
<p>이를 키오스크를 탓하기엔 애매한 부분이 있지만, 키오스크에 의존하던 수많은 매장들은, 그 날 장사를 할 수 없었고, 수많은 혼란에 휩싸이고 말았다.</p>
<p>(필자가 일하던 버거킹 역시도!)</p>
<blockquote>
<p><strong>어떤 사람이, 어떤 장소에서, 어떤 방법이든</strong>, </p>
</blockquote>
<p>조건에 관계없이 웹에 접근하여 이용할 수 있도록 보장하는 것 그것이 <strong>웹 접근성.</strong></p>
<p>키오스크가 좋은 예시가 될지는 모르겠지만, 웹 접근성의 2가지 부분을 잘 설명해줄 수 있는 좋은 예시라고 생각한다. </p>
<p>키오스크와 달리 누구나 어려움 없이 웹에 접근하여 훌륭한 웹페이지를 사용할 수 있어야하고, 
누구나 어떤 환경에서든 웹 페이지에 원활하게 접근하여 사용할 수 있어야 한다. </p>
<p>그것이 바로 <strong>웹 접근성</strong>이다.</p>
<br>
<br>

<h2 id="2-웹-접근성을-위한-개발에는-어떤-것이">2. 웹 접근성을 위한 개발에는 어떤 것이?</h2>
<p><img src="https://velog.velcdn.com/images/hayounsong/post/fbe08e54-3053-44a0-8d71-5b3536eea343/image.png" alt=""></p>
<p>대표적으로, WAI의 웹 접근성을 위한 지침들을 살펴보자.</p>
<ul>
<li>사용자가 웹의 내용을 인지할 수 있는 <strong>인지성</strong></li>
<li>탐색과 인터페이스 운용이 가능해야하는 <strong>운용성</strong></li>
<li>정보 사용과 인터페이스를 이해할 수 있어야하는 <strong>이해성</strong></li>
<li>마지막으로 다양한, 시대가 지나고도 사용될 수 있어야하는 <strong>내구성</strong></li>
</ul>
<p>이 4가지 방안에 대해서, 하나하나 부분들에 대한 개발 방향성의 예시를 들어보았다</p>
<h3 id="--인지성">- 인지성</h3>
<p>눈이 보이지 않는 시각장애인의 경우, 사운드 및 오디오를 들을 수 없는 청각장애인 역시 조건에 관계없이 웹에 사용할 수 있어야한다. </p>
<p>이러한 경우에는 사용자에게 유용하게 작용될 수 있는, &#39;대체테스트&#39;의 중요성이 높아진다고 생각한다.</p>
<p>시각장애인의 경우, 대체텍스트로 대표적으로 스크린 리더를 제공할 수 있다. </p>
<p>스크린 리더와 같은 설정을 위해서는, 웹 개발시 HTML 안에서의 meta-data 세팅이 필수적이라고 할 수 있다.</p>
<pre><code>&lt;html lang=&quot;ko&quot;&gt;</code></pre><p>이 간단한 한줄을 통해서, 스크린 리더시의 언어를 설정할 수 있다. </p>
<p>사실 저 html 태그 내의 lang 속성은 많은 이들이 간과하고 넘길 수 있는 간단한 코드이지만, </p>
<p>저 lang 하나의 설정만 제대로 되지 않는다면, 스크린 리더에는 오류가 날 것이다.</p>
<p>이외에도 사용자가 인지할 수 있을정도로 충분히 큰 텍스트를 사용한다거나, 너무 보기 힘든 색상 조합을 사용하여 사용자의 인식을 방해하는 용도는 지양해야 한다.</p>
<h3 id="--운용성">- 운용성</h3>
<p>운용성의 경우, 먼저 사용자가 프로그램을 사용하는데 방해가 없어야 한다. </p>
<p>대표적으로 너무 과하게 깜빡 꺼리는 컨텐츠가 있어 사용자의 사용을 방해한다던가, 필수적으로 시청해야 하는 영상 콘텐츠 같은 경우, </p>
<p>그 영상 콘텐츠를 재시청하거나 시점을 조절할 수 있는 등, 사용자에게 기본적인 편의를 제공하는 것은 필수적인 사항이다.</p>
<p>또한 한국형 웹 컨텐츠 지침에는 키보드만을 사용한 조작이 가능해야 한다. ( a 태그와 같이 ) </p>
<p>그리고, 컨텐츠(예를 들면 비밀번호 입력시간 제한같은 것을 너무 촉박하게 하지 않는다) 등, </p>
<p>사용자에게 충분한 시간을 제공해야 할 필요가 있다.</p>
<h3 id="--이해성">- 이해성</h3>
<p>이해성은 위 부분들과 공통되는 부분이 있다. 
먼저 사용자는 텍스트 컨텐츠를 이해하고 판독할 필요가 있다. 
이를 위해서는 웹 페이지를 개발할때 사용자가 충분히 볼 수 있는 텍스트 크기등을 설정할 필요가 있다. 
또한, 이해 및 시청을 방해하는 과한 팝업창이나 alert창 같은 것을 지향해야한다.</p>
<p>또한, 사용자의 실수를 예방하고 이를 수정할 수 있는 방안을 제시해야한다. </p>
<p>예를 들면 
사용자가 비밀번호를 까먹었을 경우, 비밀번호 찾기 기능을 제공하던가
개인정보를 잘못입력하였을 경우, 그 개인정보를 수정할 수 있는 기능을 제공하는 등을 말한다</p>
<h3 id="--견고성">- 견고성</h3>
<p>견고성을 위해서는, 유지보수가 가능한 코드를 작성하는 것이 필요하다고 생각한다. </p>
<p>협업을 할때로 예시를 들자면, 팀에서 정한 Convention에 맞춰 변수 및 속성을 정의할 필요가 있고, 
향후 다른 개발자 혹은 본인이 보더라도 이해할 수 있는 구조의 코드를 작성하는 것이 필요하다고 생각한다.</p>
<p>또한 주석관리 및 마크업 관리 역시 필수적이다. </p>
<p>향후 이 웹 앱을 유지보수 할 때 어려움을 최대한 겪지 않도록, 충분한 주석 처리 및 설명 기록이 필수적이라고 생각한다.</p>
<p>마지막으로, Semantic한 구조로 코드를 작성하여, 컴퓨터 및 인간이 이 코드에 대한 이해를 할 수 있도록 돕는 과정 역시 필수적이다.</p>
<br>
<br>

<h2 id="3-접근성을-위한-개발을-꼭-해야-할까">3. 접근성을 위한 개발을 꼭 해야 할까?</h2>
<p>필자가 장애인식 개선 교육을 들었던 때의 일이다. </p>
<p>교육을 해주신 강사님께서는 20대 때 다이빙 사고로 하반신이 마비된 이후 평생 휠체어 생활을 해야 했던 자신의 이야기를 들려주셨다. </p>
<p>어느 날은 그가 공공기관을 방문해 지하주차장에 주차를 하고 올라가려는데, 아래 사진과 같은 작은 턱을 마주하게 되었다고 한다.</p>
<p><img src="http://www.ablenews.co.kr/news/newsimages/newsimage/c_3_001420170502040053404050.jpg" alt=""></p>
<p>비장애인이 보기에는 아주 낮은 턱이지만, </p>
<p>이 턱을 마주한 순간 그 강사님은 아무것도 할 수가 없었다고 했다. </p>
<p>그에게는 그 작은 턱이 마치 높은 장벽과도 같이 느껴졌던 것이다.</p>
<p>아무리 멋진 건물을 지어놓아도 사람들이 이용하기 불편하다면 좋은 건물이 아니다. </p>
<p>웹 또한 마찬가지다. 아무리 좋은 기능과 멋진 디자인, 많은 정보들을 가지고 있다고 할지라도 사용자 경험에 있어서 차이가 생긴다면 좋은 웹 사이트라고 할 수 없다. </p>
<p>시맨틱 태그 대신 div 태그를 써도 별 문제가 없다고 생각할 수 있지만, 누군가에게는 그것이 큰 벽으로 작용할 수 있다. </p>
<p>개발을 할 때 웹 접근성을 고려하는 것은 마치 곳곳에 경사로나 점자블록을 설치해두는 것과 같다. 웹의 본질적인 가치란 무엇일지 생각해보고, </p>
<p>이를 실현하기 위해 노력했을 때 비로소 <strong>모두에게 동등한 웹</strong>을 구현하는 웹 개발자가 될 수 있지 않을까?</p>
<hr>
<p><strong>작성자</strong>
<a href="https://github.com/hayounSong">IN SOPT WEB, YB 송하윤</a>
<a href="https://github.com/ilmerry">IN SOPT WEB, YB 최은형</a></p>
]]></description>
        </item>
    </channel>
</rss>