<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jun_53.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sat, 03 Dec 2022 18:52:28 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jun_53.log</title>
            <url>https://images.velog.io/images/jun_53/profile/38fc3537-2e15-4fda-a9f3-6aeafd8f6510/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jun_53.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jun_53" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[WANTED] 프리온보딩 코스 week 4-2]]></title>
            <link>https://velog.io/@jun_53/WANTED-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%BD%94%EC%8A%A4-week-4-2</link>
            <guid>https://velog.io/@jun_53/WANTED-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%BD%94%EC%8A%A4-week-4-2</guid>
            <pubDate>Sat, 03 Dec 2022 18:52:28 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가기">들어가기</h1>
<p>이번 강의에서는 과제리뷰와 NextJS에 대한 설명이 있었습니다.</p>
<p>(해당 게시글은 실제 강의 내용을 그대로 옮겨적은것은 아니며, 주관적인 이해 및 판단으로 요약되어진 글입니다.)</p>
<h1 id="강의-section_1">강의 Section_1</h1>
<h2 id="과제리뷰">과제리뷰</h2>
<ol>
<li><p>수정 / 삭제 / 제출에 관한 버튼 처리</p>
<ul>
<li>되돌리기 어렵거나 불가능한 경우에 빨간색 레이블, Dialog등을 활용하여 재확인등을 하는것이 좋음</li>
<li>최종적인 동작을 경적하는 버튼은 화면 우측 상단과 같이 일반적으로 사용자가 실수로라도 누르기 힘든곳에 위치하는것이 좋음</li>
<li>form등에서 페이지를 벗어날때는 실수로 뒤로가는 경우등을 생각하여 재확인 경고창을 띄워주는것이 좋음</li>
</ul>
</li>
<li><p>NextJS 미들웨어 사용</p>
<ul>
<li>NextJS는 내부적인 서버가 있기때문에, 가능한 동작.</li>
<li>권한이 없는 경우 요청하는 페이지에 관한 데이터를 받지 않는것이 좋다.</li>
</ul>
</li>
<li><p>식보다는 문을 활용할수있다면, 문을 활용한 로직이 보기 편함.</p>
</li>
</ol>
<pre><code>//예시
const result = {
    page
}
if(option1){
    result.option1:option1;
}
if(option2){
    ...
}....

=&gt;

const reulst ={
    page,
    ...(option1&amp;&amp;{option1}),
    ...(option2&amp;&amp;{option2}),    
}</code></pre><ol start="4">
<li><p>Obj를 다루는것이라면 forEach보다는 map등을 사용하여 불변성을 유지할것</p>
</li>
<li><p>useQuery의 select 콜백을 사용하여 원하는 데이터를 계산해서 사용하는것이 더 좋을 수있음</p>
<pre><code></code></pre></li>
</ol>
<p>cosnt {data} = useQuery([&#39;todos&#39;,&#39;all&#39;],()=&gt;{
    return data를 뽑아내는 함수()
})
=&gt;</p>
<p>cosnt {data} = useQuery([&#39;todos&#39;,&#39;all&#39;],()=&gt;{
    select : (data)=&gt; data를 처리할 조건 및 식
})</p>
<pre><code>6. useQuery에서 셋팅할수 있는 값들은 전역에서 처리하기
(refetchOnWindowFocus 설정 및 staleTime 등 크게 수정할 부분이 없는 조건이 없다면 _app.tsx에서 관리하는 QueryClient에서 지정하기)


# 강의 Section_2

## NextJS

- NextJS는 급격하게 시장이 커져가고 있음. 2021년 초 전후로 다운로드 수가 백만이던게, 현재(2022 12월4일) 기준으로 350만 이상을 기록중
- 꾸준하고 속도감있는 업데이트, SEO, SSR등 많은 부분에서 이점을 가지고있음

### NextJS Framework

- LIb는 필요한 부분에서 불러와서 사용하지만, Framework는 작성한 코드를 프레임워크가 가져가서 대신 실행 시킨다.
![](https://velog.velcdn.com/images/jun_53/post/c7420823-66b9-4945-8dbe-895187e7231e/image.png)

예를 들어 React의 경우 동적 router, 코드스플리팅 같은것을 사용하려면 직접 구현 및 설정 해야함. 하지만 NextJS의 경우에는 따로 구현 및 설정하지 않더라도 알아서 사용하게됨.

즉, 많은 부분이 대신 구현되어있고, 사용자는 프레임워크가 허용하는 한에서 구현에 대한 걱정을 하지 않을 수 있음.

하지만 동시에 자유성이 떨어지게되는게 단점

그럼에도 NextJS는 많은 기능을 대신 해줌으로서 사용가치가 높음

NextJS가 제공하는 기능중 하나인
next/Image의 경우는 아래와 같은 장점이 있음

- Image태그의 src속성으로 받은 주소를 조작하여 NextJS 내부 서버 API쪽으로 경로를 조작하여 이미지의 용량 및 사이즈를 최적화
- 설정시 width와 height 옵션을 필수로 받아 CLS(Cumulative Layout Shift)를 방지
- NextJS의 서버내에서 한번 호출된 이미지는 서버 내 캐시에 저장되기 때문에, 많은 사용자가 사용할 경우 우수한 효과를 나타냄

- 기타 최적화를 위한 기능들
    - Compression 기능 자동지원. 렌더링 결과물 및 정적 파일에 대해 gzip압축처리
    - SWC 컴파일러를 사용하여, 기존 Babel을 사용하는것보다 Build속도가 매우 빠름
    - [추가적인 advanced 기능들](https://nextjs.org/docs/advanced-features/dynamic-import)

### Pre-rendering &amp; Data Fetching

NextJS에서 지원하는 Data Fetching의 종류는 아래와 같음.

- &lt;~~getInitialProps~~&gt; : 현재는 잘 사용되지 않으며, 공식 사이트에서도 getServerSideProps 또는 getStaticProps 사용을 권장
- getServerSideProps : 페이지 진입시 서버측에서 실행되며, 페이지를 보여주기전에 쿠키 등을 처리하거나, 미리 받아와야 하는 데이터가 있을경우 사용. 요청 헤더에 접근이 가능하기때문에, 캐싱 설정이 가능.
- getStaticProps : 서버에서 실행되는것은 같으나, 페이지를 정적 생성(SSG) 할때 사용
- getStaticPaths : Dynamic Route를 사용할 때 getStaticProps 가 생성할 경로의 목록들을 정의

해당 기능들을 사용하였을때, NextJS는 빌드시 아래와 같이 해당 페이지에서 사용된 기능들을 알려줌.
![](https://velog.velcdn.com/images/jun_53/post/52946eb3-d3c1-4ca1-a8d1-c14eac37f490/image.JPG)
![](https://velog.velcdn.com/images/jun_53/post/60434551-6738-43b9-8c31-e91d53b0350c/image.JPG)

Data Fetching된 값이 서버측에서 작동하며 Pre rendering 되는 이유는 아래와 같습니다.

1. Nextjs는 페이지 요청시 requirePage 라는 함수에 경로를 넘겨주고 페이지 컴포넌트를 가져옵니다.
2. 리턴 값중 서버 사이드 관련 메서드가 있으면 별도로 뽑아내고 실행시킵니다
3. 실행시켜 얻은 결과물을 data 변수에 담은 뒤, 서버측에서 Pre rendering시 props로 전달하게 됩니다.
</code></pre><p>const ComponentMod = await Promise.resolve().then(() =&gt;
  requirePage(pathname, distDir, serverless, isAppPath)
)
...
const { getServerSideProps, getStaticProps, getStaticPaths } = ComponentMod</p>
<pre><code>[코드출처]
(https://github.dev/vercel/next.js/blob/e933e1d211ea16c349898e141d9733b4cd06e3d8/packages/next/server/load-components.ts#L123-L140)
- requirePage를 통해 얻은 ComponentMod에서 SSR 또는 SSG와 관련된 함수를 구조 분해 할당으로 얻습니다.</code></pre><p>try {
  data = await getServerSideProps({
      req: req as IncomingMessage &amp; {
      cookies: NextApiRequestCookies
    },
    res: resOrProxy,
    query,
    ...
  })</p>
<p>```
[코드출처]
(<a href="https://github.dev/vercel/next.js/blob/e933e1d211ea16c349898e141d9733b4cd06e3d8/packages/next/server/render.tsx#L959-L974">https://github.dev/vercel/next.js/blob/e933e1d211ea16c349898e141d9733b4cd06e3d8/packages/next/server/render.tsx#L959-L974</a>)</p>
<ul>
<li>함수가 있을 경우 실행하여 data에 담습니다.</li>
</ul>
<h3 id="개발자-커뮤니티">개발자 커뮤니티</h3>
<p>단순히 React,Next 뿐만 아니라 모든 개발방면에서 활발하게 <strong>개발자 커뮤니티</strong> 가 존재함. 이러한곳을 통해 문제를 제시하거나, 새로운 기능의 추가 제안 등, 많은것 들을 활발히 토론하고있음.</p>
<p>Reactjs의 경우 개발자들의 의견을 rfcs repo를 따로 만들어  RFC 문건들을 관리중..</p>
<p>RFC(Request for Comments) 문서는 &quot;의견을 요청하는 문서&quot;라는 의미, 실제 구현에 들어가기전에 어떠한 방식으로 구현할것인지 의견을 공유하는 단계. </p>
<ul>
<li><a href="https://github.com/reactjs/rfcs/pulls">reactJS pullrequest</a></li>
</ul>
<p>NextJS에서도 마찬가지로 매우 활발하게 개발자들의 의견이 공유되며 발전되어 왔음.</p>
<p>프론트엔드 개발은 단순 NextJS 뿐만 아니라, 많은 기술적인면에서 최적화와 문제점, 효율성, 의존성 처리 등 많은 부분에서 각자 집중적인 장점을 가지는 새로운 기술들이 빠른속도로 개발중..</p>
<p>이러한 추세를 통해 점점 프론트엔드는 고도화 되어가고있으며, 프론트엔드는 점점 클라이언트 로직만을 관리하게 되지 않을것임</p>
<h1 id="마무리-소감">마무리 소감</h1>
<p>끝난지 거의 2주만에 정리글을 완성하게 됐습니다..
코스가 끝나고 곧 월드컵이 시작되고, 치맥에 취하고, 긴장이 풀리면서 정리가 많이 늦었던것 같습니다.</p>
<p>이번에는 이전과는 다르게 요약 위주로 작성해보았는데, 이전 글 보다는 뭔가 좀 불친절한거 같으면서도, 오히려 나중에 다시 되돌아보기는 더 편할것 같다는 생각이 들었습니다.</p>
<p>음.. 코스가 시작된지는 약 6주, 실제로는 4주 코스였는데 정말 많은걸 배울 수 있었고 만족도 높은 과정이였습니다.</p>
<p>총 6개의 과제물을 통해 실제 코드 실력을 늘릴 수 있었던것은 물론이고, 기존에는 새로운 스택에 대해서 왠지 모를 불안감과, 기존 코드방식에만 고집했었는데
프리온보딩 코스 과정을 통해 새로운 스택에 대한 경험을 할 수 있었고, 또한 이해하는 방식을 배웠습니다.</p>
<p>SSR을 제외하면 그래도 다 어느정도는 알고 있던 개념이라고 생각하고 있었는데, 코스를 진행하며 3시간 코스, 총 8번밖에 안되는 강의시간동안 정말 내가 모르는게 너무 많았구나 라는걸 체감 할 수 있었습니다.</p>
<p>lint와 prettier를 자동화하는 huksy, 식과 문의 사용방법,git action 및 aws를 사용한 자동화.. react가 작동하는 원리 등.. 사실상 강의에서 다뤘던 거의 대부분의 주제를 새롭게 배운듯한 느낌이였습니다.</p>
<p>이전에는 그저 함수 설명 한번 읽어보고 돌아가니까 그저 사용하고 있었다면, 그래도 이제는 어떻게 돌아가는지 이해는 하려고 노력하는 단계인것 같습니다. </p>
<p>이러한 부분을 발전이라 한다면, 첫번째 멘토님이 말씀해주셨던 <strong>시야의 확장</strong> 을 달성 할 수 있었다고 말할 수 있을것 같습니다.</p>
<p>쉬운 과정은 아니였지만, 되돌이켜 보면 정말 좋은방식의 공부였고, 힘들지만 그만큼 얻어가는 경험을 하였기 때문에, 기회가 있다면 다른 분들에게도 적극 추천해주고 싶은 코스였습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WANTED] ASSIGNMENT_6]]></title>
            <link>https://velog.io/@jun_53/WANTED-ASSIGNMENT6</link>
            <guid>https://velog.io/@jun_53/WANTED-ASSIGNMENT6</guid>
            <pubDate>Wed, 30 Nov 2022 17:35:18 GMT</pubDate>
            <description><![CDATA[<h1 id="assignment_5">ASSIGNMENT_5</h1>
<h2 id="여섯번째-과제의-요구사항">여섯번째 과제의 요구사항</h2>
<p>과제의 대략적인 요구사항은 아래와 같았습니다.</p>
<ol>
<li>대략적인 Layout만 잡혀있고, 페이지 디자인은 자율적 구현</li>
<li>Page는 총 3개로, 로그인 / 계좌리스트 Page / 계좌 상세 페이지</li>
<li>계좌 리스트 Page는 Token값이 있을때만 접근가능하고, 페이지네이션이 될것</li>
<li>계좌 리스트 Page는 옵션을 통해 검색을 비롯해 필터링이 가능할것</li>
<li>계좌 상세 정보 페이지는 수정이 가능할것</li>
<li>브로커명을 비롯해 여러 데이터들은 json 파일을 참조하여 데이터에 맞게 문자를 변환하고, 계좌 번호는 앞위 각 2글자씩을 제외하고 * 처리할것</li>
</ol>
<ul>
<li>이번 과제는 과제기한이 5.5일로 기존 과제보다 조금 더 길게 시간이 주어졌습니다.</li>
<li><ul>
<li>이번 과제에서는 TS와 NextJS, ReactQuery가 권장되었기때문에, 해당 기술을 사용하였습니다.</li>
</ul>
</li>
<li>++ API 서버는 별도로 주어지지 않으며, json 파일만을 가진 repo를 json 서버로 구동하여 로컬환경에서 작업할것</li>
</ul>
<h2 id="시작하기에-앞서">시작하기에 앞서</h2>
<h3 id="초기-셋팅">초기 셋팅</h3>
<p>초기 셋팅은 이전 과제물들과 유사하게 진행하였습니다.
팀원 한분이 불필요한 파일을 제거하고, ESLint,prettier,husky 설정이 된 repo를 생성하고 main브런치에 올린뒤, 팀원들이 각자 클론하여 브런치를 생성하여 각자의 브런치에서 진행하였습니다.</p>
<h2 id="코딩구현">코딩구현</h2>
<h3 id="1-공통-레이아웃-구현-및-애니메이션-구현">1. 공통 레이아웃 구현 및 애니메이션 구현</h3>
<p>제가 구현한 레이아웃은 총 4부분으로 되어있습니다. 헤더 / 메인(contents) / 푸터 /사이드 메뉴 Bar</p>
<p>Layout 파일을 만들고 그곳에 저 4개의 컴포넌트를 지정하고, 메인은 react와 유사하게 children을 통해 공용으로 사용하였습니다. next는 outlet이 없기 때문에 약간의 사용방법을 익히는데 시간이 들긴 하였습니다.</p>
<p>하지만 그것보다는 사이드바 의 애니메이션을 구현한게 마음에 들었던것 같습니다.</p>
<p>전체적인 구상은 사이드 바를 on/off 할 수 있고, 그 때마다 사이드 바는 translate를 통해 움직이고 동시에 헤더,메인,푸터도 같이 움직이는 형식입니다.</p>
<p>초기에는 각각의 컴포넌트마다 absolute를 주고, 각각 애니메이션을 지정해주었지만, 생각해보니 그냥 wrapper를 2개를 만들어서 그 두개에만 이벤트를 주면 되겠구나 하는 생각이 들어 리팩토링을 하게 되었습니다.
(구현은 아래와 같습니다.)</p>
<pre><code>    &lt;LayoutWraaper&gt;
      &lt;SlideBarWrapper show={show}&gt;
        &lt;SideBar /&gt;
      &lt;/SlideBarWrapper&gt;

      &lt;MainWrapper show={show}&gt;
        &lt;Header setShow={setShow} show={show} /&gt;
        &lt;ContentsWrapper&gt;{children}&lt;/ContentsWrapper&gt;
        &lt;Footer /&gt;
      &lt;/MainWrapper&gt;
    &lt;/LayoutWraaper&gt;</code></pre><p>가장 상위의 Layout에 relative를 주었고, SlideBarWrapper와 MainWrapper에 각각 absolute를 주고, 애니메이션은 아래와 같이 구현하였습니다.</p>
<pre><code>const SlideBarWrapper = styled.div(({ show }: { show: boolean }) =&gt; [
  show ? tw`translate-x-0 ` : tw`-translate-x-full`,
  ...
  ]
  const MainWrapper = styled.div(({ show }: { show: boolean }) =&gt; [
  show ? tw`translate-x-[256px] ` : tw`translate-x-0`,
  ...
  ]</code></pre><p>Slidebar가 on됐을시 그 만큼만을 Main에서 밀어주면 되고,Off시는 제자리로 오게 하면 되는 간단한 transform 처리이지만, 많이 다뤄보지 않았던 애니메이션이기도 하였고, 기존에는 그저 인터넷 소스를 가져와 사용하는 방식이였지만, 이번에는 직접 애니메이션을 어떻게 구현할지 구상하고 구현해본게 마음에 들었던것 같습니다.</p>
<h3 id="2-페이지-네이션">2. 페이지 네이션</h3>
<p>페이지네이션 자체도 사실 구현이 어려운편은 아니지만, 역시 기존에는 다른 Lib등을 사용하다 직접 구현해보려니 이게 생각만큼 구현이 잘 되진 않아서 약간 헤맸던것 같습니다.
(구현은 아래와 같습니다.)</p>
<pre><code>const VIEW_PAGE_COUNT = 5;
const LIMIT = 10;

export function pagination(count: number) {
  let totalCount = Math.ceil(count / LIMIT);

  let originCntArr = [];
  let resultArr = [];
  for (let index = 1; index &lt;= totalCount; index++) {
    originCntArr.push(index);
  }
  let totalPage = Math.ceil(originCntArr.length / VIEW_PAGE_COUNT);

  for (let index = 0; index &lt; totalPage; index++) {
    let spliceArr = originCntArr.splice(0, 5);
    resultArr[index] = spliceArr;
  }
  return resultArr;
}</code></pre><p>count는 총 데이터 배열의 값입니다. LIMIT는 한 페이지에서 보여줄 데이터의 양이고, VIEW_PAGE_COUNT는  한번에 보여줄 버튼의 갯수입니다.</p>
<p>구현에 있어 아쉬운건 for문을 두번 돌린건데, 돌이켜보니 for문에서 index=totalPgae를 주고, index-(VIEW_PAGE_COUNT)를 했다면 for문을 한번만 돌리고 완성 할 수 있엇을텐데 구현이 좀 아쉬운것 같습니다.</p>
<p>추가적으로, 버튼 이동시 데이터가 패칭되고, 활성화 버튼값이 유지되어야 하기 때문에, 아래와 그 부분은 아래와 같이 구현하였습니다.</p>
<pre><code>  const page = router.query.page ? parseInt(router.query.page as string, 10) : 1;
  const defaultOffset = Math.ceil(page / 5) - 1;
  const [offSet, setOffSet] = useState(0);

    useEffect(() =&gt; {
    setOffSet(defaultOffset);
  }, [defaultOffset]);</code></pre><p>(offset으로 버튼이 있어야 하는 위치를 나타내었습니다. 1<del>5 or 6</del>10 등..)
(useState의 기본값으로 defaultOffset을 주면 초기값이 제대로 잡히지 않아, useEffect를 사용하여 offset값을 설정하였습니다.)</p>
<pre><code>  const paginationRender = (pagiArr: number[]) =&gt; {
    return pagiArr.map((pageNumber: number, idx: number) =&gt; (
      &lt;PaginationLi
        isActive={pageNumber === page}
        onClick={() =&gt; router.push(`?page=${pageNumber}`)}
        key={`${pageNumber}_${idx}`}
      &gt;
        {pageNumber}
      &lt;/PaginationLi&gt;
    ));
  };</code></pre><p>페이지네이션 렌더부분, router를 통해 url의 page를 읽어오고 렌더되는 컴포넌트의 pageNumber와 page가 동일하면 active효과를 주었습니다.</p>
<h3 id="3-react-query를-사용한-구현">3. react-query를 사용한 구현</h3>
<p>이번 프로젝트에서 react-query는 단순 데이터 패칭 뿐 아니라, SSR에서도 사용하였습니다. 
react-query에서 관리하는 queryClient의 dehydrateState를 사용하는방식인데, 사용하기위해 _app.tsx에서 Hydrate 설정을 하였습니다.</p>
<p>SSR을 사용한 부분은 AccountList. 계좌목록에서 사용하였습니다.</p>
<pre><code>export const getServerSideProps: GetServerSideProps = async context =&gt; {
  const queryClient = new QueryClient();
  const page = context.query.page ? parseInt(context.query.page as string, 10) : 1;
  await Promise.all([
    queryClient.prefetchQuery([&#39;userList&#39;], userAPI.getUser),
    queryClient.prefetchQuery([&#39;AccountList&#39;, 1], () =&gt; accountAPI.getList(page)),
  ]);
  return {
    props: {
      dehydratedState: dehydrate(queryClient),
    },
  };
};</code></pre><p>(구현부분)
페이지 접근을 단순히 컴포넌트를 통한 이동뿐만 아니라 URL을 통한 이동으로도 할 수 있기 때문에 context를 통해 query의 page param을 가져와 사용하거나, 기본값을 사용하였습니다.</p>
<p>prefetchQuery를 사용하여 서버단에서 데이터를 읽어오고, Promise.all을 사용하여 두 데이터 모두 정상적으로 처리 되었을경우에만 정상값을 리턴시키도록 하였습니다.</p>
<p>그 외 값이 SPA에서 데이터를 패칭하는 useQuery사용은 아래와 같이 단순하게 사용하였습니다.</p>
<pre><code>export const useAccountList = (page: number) =&gt; {
  const queryData = useQueries({
    queries: [
      { queryKey: [&#39;userList&#39;], queryFn: userAPI.getUser, staleTime: 1000 * 60 },
      {
        queryKey: [&#39;AccountList&#39;, page],
        queryFn: () =&gt; accountAPI.getList(page),
        staleTime: 1000 * 60,
      },
    ],
  });
  const [userData, accountData] = queryData;

  return [userData, accountData];
};</code></pre><p>(페이지가 변할시 새로운 계좌 데이타를 가져오는 부분)
staleTime은 _app.tsx에서 한번에 지정할 수도 있지만, 각 데이터 마다 stale타임을 명시해주는것도 좋다 생각하여 따로 적어주었습니다.</p>
<h3 id="좋았던-부분-및-개선된-부분">좋았던 부분 및 개선된 부분</h3>
<ol>
<li>tailwind-styled-components는 TS에서 결점이 있어서, 이전부터 사용해보고 싶었던 twin.macro를 사용하여 스타일링적인 구현부분은 상당부분 개선된게 느껴졌습니다.</li>
<li>주 스택이 아니였던 NextJS, React-query를 사용하여 결과물을 냈단점이 좋았습니다</li>
<li>Transform 애니메이션을 어떻게 사용해야 하는지에 대해서 전체적인 감을 잡을 수 있었습니다.</li>
<li>NextJS에서 SSR 및 SSG를 어떻게 사용하는지, 그리고 CORS는 어떻게 방지하는지 등. Next를 실전에서 어떻게 사용하는지에 대한 경험을 하게되었습니다.</li>
<li>NextJS의 ServerSideProps가 아닌, React-query의 Hydrate state를 통한 SSR을 알게됐고, 구현까지 해보게 되어 좋았습니다.</li>
<li>단순히 SPA적인 접근으로 데이터를 받아오는게 아닌, URL로 특정 데이터로도 접근 가능하게 구현한게 좋았습니다.</li>
<li>전체적으로 이번 프로젝트에는 해보고 싶었던 모든 새로운 스택에 도전해서, 스택에 대한 낯섬을 해소하고, 이해와 결과물을 얻었다는게 좋았습니다.</li>
</ol>
<h3 id="아쉬웠던-부분">아쉬웠던 부분</h3>
<ol>
<li><p>처음 적용하는 스택의 설정과 낯설음 등에 의해서 초기에 시간과 에너지를 너무 소비하였고, 결국 과제를 미완성으로 끝마치게 된게 아쉬웠습니다.</p>
</li>
<li><p>미완성 뿐만 아니라 전체적으로 코드가 완성도가 많이 떨어진다고 느꼈습니다. 로직이 너무 분리되지 않고 구현을 위한 코드같은 느낌이였습니다. </p>
<p> 이런 부분은 시간적 여유가 생겨 리팩토링적인 여유가 있으면 물론 신경쓰겠지만, 그렇지 않더라도 코드를 작성할때 조금만 더 신경써서 코드의 로직을 분리하면 어땠을까 하는 생각을 했습니다.</p>
</li>
<li><p>구현적인 부분에서 로그인 / 로그아웃은 결함이 있는채로 작성된게 아쉬웠습니다. 기본적으로 json토큰의 유효기간을 처리를 하지 못해서 각 페이지에 대한 접근처리를 아예 빼버렸는데, 이부분도 middleware를 사용해서 처리하면 어땠을까 하는 아쉬움이 있었습니다.</p>
</li>
<li><p>클린코드적인 측면에서 Http를 비롯해서, 데이터를 관리하는것은 class로 해보고 싶었는데, 도중에 에러를 해결하지 못하여 다른 방법으로 우회해서 요구사항을 구현한게 아쉬웠습니다.</p>
</li>
<li><p>아직은 리액트 쿼리의 사용방법이 미숙하다고 느꼈습니다. useQueries를 사용할경우 여러 쿼리에 대한 비동기 방법을 아직은 잘 감이 안오는것 같습니다.
미구현한 부분이 있기 때문에 구현을 못한것도 있지만, 하나의 객체에 캡슐화를 통해 CRUD를 완벽히 관리하였으면 좋았을것 같다고 생각하였습니다.</p>
</li>
<li><p>TS에 대해서 아직 부족한점을 느꼈습니다. 일부데이터에서 끝내 해결하지 못해 any를 명시한게 있었습니다.</p>
</li>
<li><p>새로운 스택을 사용하는데 생각보다 너무 적응이 느리다고 느꼈습니다. 돌이켜보면 그저 하라는데로만 하면 되는건데, 너무 헤맸던것 같습니다.</p>
</li>
</ol>
<h2 id="배포-링크">배포 링크</h2>
<p>(작업 기간 약 5.5일)
<a href="https://pre-onboarding-7th-3-2-9-three.vercel.app/">https://pre-onboarding-7th-3-2-9-three.vercel.app/</a>
<code>id : 1234@1234.com / pwd : 1234</code>
<code>json서버를 팀단위로 공용으로 사용하고있는데, 이 데이터 중 마지막 3페이지 정도 데이가 제가 작성한 코드의 타입과 맞지 않아 에러가 발생하는 이슈가 잇습니다.</code></p>
<h2 id="git-repo">Git Repo</h2>
<p><a href="https://github.com/jun-05/pre-onboarding-7th-3-2-9">https://github.com/jun-05/pre-onboarding-7th-3-2-9</a></p>
<h2 id="bestpractice-선정-결과">BestPractice 선정 결과</h2>
<p>완벽히 구현하신 팀원 분이 한분밖에 없으셔서, 그분이 선정되셨습니다.</p>
<h2 id="진행-중-아쉬웠던-점">진행 중 아쉬웠던 점</h2>
<ul>
<li>팀원 중 두세분정도가 각자 사정이 생기셔서 원활하게 참여하지 못하셨던게 아쉬웠던것 같습니다.</li>
<li>저도 비록 미완성으로 배포하긴 했지만, 다른분들도 미완성이더라도 시간내에 배포하는게 좋지 않았을까 하는 생각이 있었습니다.</li>
</ul>
<h2 id="진행-중-좋았던-점">진행 중 좋았던 점</h2>
<ul>
<li>이번 프로젝트에서는 많은 분들이 Next와 React-query를 처음으로 사용하게 되었는데, 그러다보니 많은 부분에서 막히는 부분이 많았습니다. 하지만 막혔던 부분에 대한 해결방법 공유와 Repo공유 등, 기존 프로젝트중에서 가장 활발하게 정보를 공유했던것 같습니다.</li>
<li>각자 프로젝트 진행상황 공유시간에 막히는 부분에 대해서 다 같이 의견을 내고 해결방안을 궁색하는 시간이 좋았던것 같습니다.</li>
</ul>
<h1 id="전체-프로젝트-마무리-소감">전체 프로젝트 마무리 소감</h1>
<p>개인적인 팀 프로젝트 소감은 사실 한줄로도 충분할것 같습니다. 짧은 시간내에 몰입하여 개발하는 경험을 통해 많은것을 배우고, 공부 또는 개발하는 방식을 배울 수 있었던것 같습니다.</p>
<p>하지만 팀으로서의 프로젝트적인 면을 봤을때는 할말이 좀 많을것 같네요.
한달이 좀 안되는 시간내에 짧고 굵은 경험을 한것 같습니다. 
지금까지는 대부분 혼자서 공부하거나, 클론 코딩을 하거나, 대부분 혼자하는 경우가 많았는데 생면부지의 팀원 8명이 랜덤으로 배치되 제로부터 시작해 6개의 프로젝트를 제출해가는동안 규칙을 만들고, 틀을 점차 형성해가면서 팀으로서의 성장을 느낄 수 있었던것 같습니다.</p>
<p>처음에는 BestPractice를 어떻게 뽑아야 하는지부터 몇시간을 헤매고, 토의한것에 대해선 전혀 정리하지도 못하였고, 코드의 형식 또한 제각각이였지만</p>
<p>팀으로서 진행해가며 토의에는 꼭 필요한 안건만 말하게 되면서 시간이 짧아지게 되었고, </p>
<p>토의한것은 정리해서 올려 언제든 볼수있게 하여, 토의에 참여 못 하였거나, 토의한 내용이 기억이 안나면 나중에라도 볼 수 있게 하였습니다.</p>
<p>만나는 시간은 처음에는 제각각이였지만, 점차 만나는 시간은 고정되어 안정되감을 느꼈고</p>
<p>readME 디자인이 부족한거 같아 다른팀의 readME에서 좋은 점을 가져와 readME 템플릿을 만들기도 하였습니다.(지나고 보니 약간 촌스럽긴 한거 같습니다.)</p>
<p>초기에는 팀원들간의 소통이 잘 안되어 답답하고, 스트레스를 받았던것도 사실이지만, 진행해가며 조금씩 팀으로서의 정체성 같은것을 느낄 수 있었던것 같습니다.</p>
<p>하지만 아쉬운 점도 있습니다.
저는 팀장을 맡아 진행하였는데, 사실 경험자체가 거의 없는 편이였기때문에 초기엔 토의가 불필요하게 길어졌던것 같습니다.</p>
<p>시간 분배도 초기엔 너무 생각없이 잡았던것도 있습니다. 오전 10시에 시작한 강의가 2시에 끝났는데 10분후에 보자고 한게 기억에 남습니다.</p>
<p>그리고 초기에 무리한 안건을 낸것 등도 기억에 남는 아쉬운점인것 같습니다. BestPractice의 코드를 다 같이 공부하자느니, 각자 팀원의 코드를 보 Discussion에 BestPractice라 생각하는 코드 부분을 적어 올리자느니 하는 부분이 있었습니다. </p>
<p>전체적으로 팀장으로서의 부담감 때문인지, 초기에는 더욱 오버페이스로 팀을 이끌어나가려고 했었던것 같습니다.</p>
<p>여러가지 시행착오가 있을뻔했지만, 그래도 그때마다 팀이라는 가치를 깨달을 수 있었습니다.</p>
<p>제가 이상할 수 있는 안건을 내더라도, 팀원분들이 그 안건에 대한 수정안을 말해주셨고, 그 의견들을 수렴해가며 하나의 규칙, 틀을 만들 수 있었던것 같습니다.</p>
<p>돌이켜 보면 잘한 부분, 못한 부분 모두 다 성장의 일부였다는 생각이 드네요.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WANTED] 프리온보딩 코스 week 4-1]]></title>
            <link>https://velog.io/@jun_53/WANTED-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%BD%94%EC%8A%A4-week-4-1</link>
            <guid>https://velog.io/@jun_53/WANTED-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%BD%94%EC%8A%A4-week-4-1</guid>
            <pubDate>Wed, 30 Nov 2022 10:49:42 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가기">들어가기</h1>
<p>이번강의에서는 MPA,SPA,SSR 등을 비교하고,Univeral Rendering을 다루었습니다.</p>
<p>(해당 게시글은 실제 강의 내용을 그대로 옮겨적은것은 아니며, 주관적인 이해 및 판단으로 요약되어진 글입니다.)</p>
<h1 id="강의-section_1">강의 Section_1</h1>
<h2 id="mpamulit-page-aplication">MPA(Mulit Page Aplication)</h2>
<p>현재 프론트엔드에서는 SPA를 많이 사용하는 추세기때문에, 많이 사용되지는 않지만, 이전에는 하나의 정적 페이지를 실제로 여러개 만들고, 요청이 들어오면 그에 맞는 페이지를 응답하였습니다.</p>
<p>JSP와 같이, Index / Auth와 같이 각각의 html파일을 만들어 주어진 역할만을 수행하는 방식입니다. </p>
<p>MPA와 같은경우 간단하게 html + CSS + JS정도만으로 처리가 가능합니다. 
이러한 방식을 사용할경우 필요한 Lib가 적어지고, 정적 페이지를 그대로 노출하기때문에, SEO(Search Engine Optimization)적인 면에서는 이점일수 있습니다. MPA는 만들어진 결과를 노출시키기 때문에, SSR방식 입니다.</p>
<p>단, MPA와 같은 경우, 새로운 요청을 보낼경우 새로운 HTML파일을 받고 그것을 렌더링 하는 과정이 필요하기때문에, 요청사이에 빈 화면이 노출되게 됩니다. </p>
<p>또한, 데이터의 추가 수정등의 데이터 변경 요청을 보냈을때 요청이후 변경 사항이 클라이언트측에서 갱신되게 하기 위해서 페이지를 새로 고침하는 방식이였습니다.
form의 submit 버튼이 클릭시 page를 새로고침 하는 이유이기도 합니다.</p>
<p>이러한 단점을 보완하기 위해서 Ajax가 도입되었고 요청 이후에 바로 변경 사항을 갱신할 수 있게 되었습니다.</p>
<h1 id="강의-section_2">강의 Section_2</h1>
<h2 id="spa-with-csr">SPA with CSR</h2>
<p>시간이 흘러, 개개인의 컴퓨터 사양이 급속히 올라가며, 이전보다 더 클라이언트측에 역할을 맡기는 개발환경이 되었습니다.</p>
<p>그런환경속에서 클라이언트 측에 렌더링 역할을 맡기는 CSR(client side rendering)을 사용하는 SPA(sing page application)이 탄생하게 되었습니다.</p>
<p>CSR을 사용하는 SPA의 장점으로는 요청시 빈 페이지(흰 화면)을 보여주지 않음으로서 사용자의 UX가 좋아지며, 기존 매 요청마다 HTML을 주고 받는 형식의 불필요한 통신이 줄어든다는 장점이 있습니다.</p>
<p>이것을 구현하기 위해서 CSR을 사용하는 SPA는 다음과 같은 과정을 거칩니다.</p>
<ol>
<li>사용자가 SPA을 사용하는 페이지에 초기 요청을 보냅니다.</li>
<li>웹서버는 요청에 대해 head를 제외한 body부분이 비어있는 index.html을 반환합니다.</li>
<li>클라이언트 측 브라우저는 head를 읽으며 추가로 필요한 자원(js,css)을 서버로 다시 요청합니다.</li>
<li>다운로드가 완료되면, CSS와 자바스크립트는 입력된 로직에 따라 화면을 렌더링 하기 시작합니다.</li>
</ol>
<p>여기서 초기 요청 이외에 다른 요청을 할경우 아래와 같은 추가 과정을 거칩니다.
5. 요청한 API에서 데이터를 받아와 index.html의 데이터를 새롭게 교체하고 데이터에 맞게 렌더링 합니다.
6. 만약 페이지 이동이 필요한 경우, 기본적인 HTTP GET 요청을 막아 페이지 이동은 제한하고, 브라우저 주소는 변경된 상태를 유지하도록 합니다. 그리고 요청을 통해 받은 데이터를 통해 새롭게 페이지를 렌더링 합니다.</p>
<p>이러한 방식은 아래와 같이 정리할수 있습니다.</p>
<ul>
<li>최초 통신시 JS와 CSS를 다운받아 기본적으로 화면을 렌더링 하기 위한 준비를 합니다.
(JS는 초기에 모든 코드를 받는것이 비효율적일 수 있기때문에, 코드 스플리팅을 통하여 다운로드되는 JS를 제한하기도 합니다.)</li>
<li>통신이 들어오면 준비된 JS와 CSS, 그리고 추가적인 Img 와 데이터 등의 리소스를 요청하여 화면을 렌더링합니다.</li>
</ul>
<h2 id="crp">CRP</h2>
<p>이전 강의에서 소개되었듯이 CRP는 브라우저가 화면을 렌더링하는 과정입니다.</p>
<p>다시금 정리하면, </p>
<ol>
<li>초기에 HTML과 CSS 문서로부터 정보를 받아와 각각 DOM과 CSSOM(CSS Object Model)을 생성합니다.</li>
<li>이 둘을 합쳐 렌더 트리로 변환합니다.</li>
<li>브라우저는 요소들이 위차할 자리를 계산하는 Layout 을 합니다.</li>
<li>또한, 화면을 실질적으로 px로 표시하기 시작하는 Painting 을 합니다.(Layout의 하위단계)</li>
<li>최종적으로, 여러 레이어에서 작업된 Layout과 Painting을 하나의 화면으로 합성(composite)하여 렌더링이 완료됩니다.</li>
</ol>
<p>이러한 과정은 화면 변경시 변경되는 수준에 맞게 재실행됩니다. 만약 width만 변경된다면 Layout 이하의 단계가 새롭게 실행되며, 색상만이 변경된다면 Painting 이하의 단계만이 실행됩니다.</p>
<p>만약 MAP와 같이 새로운 요청시 새로운 페이지를 요청하게 된다면 이러한 과정은 매번 일어나게 될것이고, 비효율적입니다.</p>
<p>또한, 일반적인 JS 및 Jquery를 사용하게 될 경우도, 사소한 하나의 변화에도 매번 실행되기 때문에 비효율적 입니다.</p>
<p>이러한것을 효율적으로 실행하기 위해서 리액트는 JS객체로 이루어진 가상돔(virtual DOM)을 사용하고 있습니다.</p>
<p>리액트가 가상돔을 사용하는 방법은, 만약 부모 Props가 교체되었다면, 하위 자식컴포넌트들은 기본적으로 모두 교체가 되는것으로 간주합니다.</p>
<p>변경되는 변화들은 리액트의 스케쥴러에 의해서 한번에 모아진다음, 가상돔에 재조정(Reconciliation) 과정을 가지고, 이것을 실제 DOM에 전달하여 CRP를 최소화 합니다.</p>
<p>이러한것을 실행하기 위해서 리액트는 불변성을 전제로 하고 있습니다.</p>
<p>변화들을 감지하는 방법으로
일일이 가상돔의 변경된 부분을 모두 찾아 비교하는것보단</p>
<p>변경시 새로운 가상돔을 생성하여, 얕은 비교를 통하여 바뀐 부분만을 교체하는것이 훨씬 효율적이기 때문입니다.</p>
<p>리액트는 CSR을 사용하는 SPA 앱이며, 
또한 위와 같은 방식을 통하여 가상돔을 사용한 CRP를 효율적으로 관리하고 있습니다.</p>
<h1 id="강의-section_3">강의 Section_3</h1>
<h2 id="ssr">SSR</h2>
<p>MPA에서도 언급했듯이, SSR은 결과물이 완성되어 있는 HTML파일을 전해줍니다.
리액트와 같은 SPA은 비어있는 HTML문서를 전해줍니다.</p>
<p>이러한 두 방식의 차이는 몇가지가 있지만, 크게 3가지로 볼 수 있을것 같습니다.</p>
<ol>
<li><p>많은 검색 크롤러는 SSR에서 제대로 작동합니다. CSR 페이지로의 접근시에는 초기 값이 비어있기때문에, 그대로 넘어가는 경우가 많기 때문입니다.</p>
</li>
<li><p>초기 요청시, CSR은 필요한 재료(js,css등)을 다 받고 작동하기 때문에 FCP에서 TTI까지의 시간이 오래걸리지만, SSR은 초기에 미리 렌더링이 완성된 HTML을 제공하기때문에, FCP 및 TTI 시간이 짧습니다.</p>
<ul>
<li>FCP (First Contentful Paint) - 텍스트, 이미지등 페이지가 로드되기 시작한 시점부터, 컨텐츠 일부가 화면에 렌더링 되기 시작한 시점의 시간</li>
<li>TTI (Time to Interactive) - 앱이 사용자와 상호작용할 준비가 완료된 시점(이벤트가 이벤트 리스너에 제대로 연결되는 시점)</li>
</ul>
</li>
<li><p>CSR은 클라이언트측에서 대부분의 로직을 처리하고 렌더링 하지만, SSR은 서버측에서 로직을 처리하고 렌더링한 결과를 반환하기때문에 서버측에 부담이 많이 가게 됩니다.</p>
</li>
</ol>
<h2 id="ssr과-ssg">SSR과 SSG</h2>
<p>SSR은 요청이 들어올때마다 렌더링이 일어나지만,
SSG는 개발자의 빌드 요청시에 렌더링이 일어나게됩니다.</p>
<p>즉, SSR은 매 요청시마다 서버가 요청에 맞는 로직을 실행하고 렌더링된 값을 반환하며, SSG는 매 요청시마다 서버가 준비해둔 렌더링 값을 반환합니다.</p>
<p>SSR은 변화가 적은 시간내에 잦은 경우 사용될수있으며, SSG는 변화가 적은 경우 사용될 수 있습니다.
SSG를 사용하는경우 CI/CD를 사용하여 정해진 주기마다 빌드를 통해 새로운 데이터를 받아 새롭게 렌더링된 페이지를 준비해둘수 있습니다.</p>
<h1 id="강의-section_4">강의 Section_4</h1>
<h2 id="universal-rendering">Universal Rendering</h2>
<p>Universal Rendering 이란, 하나의 환경에서 CSR과 SSR을 함께 지원하는것을 말합니다.</p>
<p>React와 같은경우 react-dom/server를 사용하여 이것을 구현하고 있습니다.</p>
<p>SSR은 서버측에서 자원을 마련하여, Index.html에 값을 채워 보내준다는것입니다. 때문에 SSR을 위해서는 기존의 CSR에서는 필요 없던 내부 리액트 server가 필요하게 됩니다.</p>
<p>대략적인 설명으로서, </p>
<p>리액트 서버에서는 우선, SSR을 할 컴포넌트에 대한 요청이 들어오면 해당 컴포넌트를 직렬화 하게됩니다. 
<code>직렬화(Serialize) 란 어떠한 객체를 송수신이 가능한 형태로 변환해주는것을 말합니다. 문자열을 JSON, XML등의 형식으로 변환해준다는것을 떠올리면 될것 같습니다. 리액트 서버에서는 renderToString함수를 통해 React컴포넌트를 HTML 문자열로 직렬화 합니다.</code>
그리고 직렬화를 통해 얻은 컴포넌트 값을 서버측에서 읽어들인 HTML파일의 문자열 <code>&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;</code> 사이에 HTML로 변환된 replace를 사용하여 끼어 넣습니다.</p>
<p>그리고 해당 파일(문자열)을 status(200)과 함께 서버응답으로 내보내게 됩니다.</p>
<p>이렇게 서버측에서 HTML파일은 완성되어 주어지지만, 상호작용(Interaction)이 가능한 함수들이 있을경우클라이언트 측에서 수화(Hydrate) 과정을 거쳐, 페이지가 최종적으로 완성되어지게 됩니다. 
수화 과정이 끝나는 시점은 TTI를 의미합니다.</p>
<p><code>수화(Hydrate)란 직렬화된 이벤트 함수를 실제 렌더링된 HTML을 읽어 이벤트 함수가 달려 있어야 하는 DOM들의 위치를 찾아 이벤트 리스너를 달아주는 것입니다.</code></p>
<p>정리하자면, SSR은 서버측 React에서 HTML 템플릿을 문자열로 읽고, SSR하고자 하는 컴포넌트의 값을 직렬화해서 얻은 값을, HTML 문자열의 적절한 위치에 값을 교체하고, 서버 응답으로 실어서 내보낸다는 것입니다.</p>
<p>이후, 클라이언트측에서는 수화(Hydrate) 과정이 필요하다면 과정을 거쳐 페이지를 최종 완성하게됩니다.</p>
<h1 id="마무리-소감">마무리 소감</h1>
<p>기존 MPA는 이전에 자바의 스프링과 바닐라 JS를 사용한 프로젝트를 했을때 그런방식으로 구현하였던 경험이 있었습니다
그때는 별 생각없이 했던 form의 submit시 페이지 이동 또는 새로고침등에 대해서 별 의문을 안 가졌었는데
이번 강의를 통해 뒤늦게 나마 의미를 알게 되었네요.</p>
<p>React가 CSR이고 SPA라는것, 가상돔을 사용한다는것에 대해서도 매우 중요하고, 기본적인것이니 만큼 알고는 있었지만, 왜 그런 방식을 택하였는지와  그것에 대한 이점, 그리고 작동방식에 대해서는 명확하게 알지 못하였는데 이 강의를 통해서 이해하게 되어 좋았습니다.</p>
<p>SSR은 NextJS를 사용하면 알아서 해주는 해주는 마법같은 테크닉이라고 막연히 생각하였습니다. 이번 강의를 통하여 SSR이 어떠한 방식으로 돌아가는지 알게 되었는데, 마술의 트릭을 알게된것마냥, 생각보다 간단하면 간단하고, 대단하다면 대단한. 어쨌거나 이번 코스의 기본 목표였던 시야의 확장을 이번에도 깨우쳤던것 같습니다.</p>
<p>추가적으로, SSR 이외에 SSG를 배웠고, SSG와 같은경우 SSR과 같이 SEO적인 측면에서도 좋고, CDN에서 캐시된 데이터를 반환해주기 때문에 서버측 부담도 덜 들어간다는것을 알게되었는데, 이후에 블로그 또는 어떠한 글을 긁어오는 Page등을 만들시 사용하면 좋겠다라는 생각을 해보았습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Tailwind CSS] twin.macro 설치 하기 with react / next]]></title>
            <link>https://velog.io/@jun_53/Tailwind-CSS-twin.macro-%EC%84%A4%EC%B9%98-%ED%95%98%EA%B8%B0-with-react-next</link>
            <guid>https://velog.io/@jun_53/Tailwind-CSS-twin.macro-%EC%84%A4%EC%B9%98-%ED%95%98%EA%B8%B0-with-react-next</guid>
            <pubDate>Mon, 28 Nov 2022 11:33:51 GMT</pubDate>
            <description><![CDATA[<h1 id="twinmacro">twin.macro</h1>
<p><a href="https://www.npmjs.com/package/twin.macro">npm 문서</a></p>
<p>twin.macro는 emotion 또는 styled-componet Lib에 대해 의존성을 가집니다. 그렇기 때문에 설치를 하기 위해선 다른 Lib를 설치할 필요가 있습니다.</p>
<p>또한, 약간의 babel 설정 추가가 필요합니다.
공식문서의 하단을 보면 각각의 환경에 맞는 시작 방법 링크가 적혀있습니다.</p>
<h2 id="cra환경에서-설치">CRA환경에서 설치</h2>
<p>우선, CRA환경에서 styled-componets를 사용하는 twin.macro 설치 방법을 알아보겠습니다.</p>
<ol>
<li><p>기본적으로 tailwind와 같이 사용되는 Lib 추가와, config 설정 및 import 설정이 되어있어야합니다. (첫번째 글 참조)</p>
</li>
<li><p><code>npm install twin.macro styled-components</code> 를 통해 twin.macro와 styled-components를 설치하고, babel-plugin-macros를 dev설정으로 설치합니다.
<code>TS를 사용하는경우, @types/styled-components 도 dev설정으로 같이 설치해주어야 합니다.</code></p>
</li>
<li><p>babelMacro 설정을 통해 twin.macro가 styled-components 을 사용할 수 있도록 해주어야 합니다. 2가지 방법이 있습니다.
3-1. 프로젝트 최상단에 babel-plugin-macros.config.js을 생성하여 아래와 같은 코드를 추가합니다.</p>
<pre><code>   // babel-plugin-macros.config.js
   module.exports = {
   twin: {
    preset: &#39;styled-components&#39;,
   },
   }</code></pre><p>3-2. pacakge.json 파일에 아래와 같은 코드를 추가합니다.</p>
<pre><code>// package.json
 &quot;babelMacros&quot;: {
   &quot;twin&quot;: {
     &quot;preset&quot;: &quot;styled-components&quot;
   }
 },</code></pre></li>
</ol>
<p>둘다 기본적으로 babel-plugin-macros를 사용하여 내부적으로 twin.macro가 사용할 Lib를 연결해주는 설정입니다.
<a href="https://gigibean.tistory.com/75">관련 정보</a></p>
<p>Styled-components로 생성한 컴포넌트는 unique한 클래스 값을 가진 무작위 네임으로 바뀌어버립니다.
때문에 이전 tailwind-styeld-components를 사용한 dark 모드방식이 작동하지 않습니다.</p>
<p>(이전방식)</p>
<pre><code>  const DarkModeWrapper = tw.div`
      ${(p) =&gt; (p.dark ? &quot;dark&quot; : &quot;&quot;)}
  `;</code></pre><p>이런 상황에서 twin.macro를 사용할경우 해결방법은 두가지로 나뉘게 됩니다.</p>
<ol>
<li>tw.div로 컴포넌트화만 하고, className은 직접 명시하는 방법입니다.<pre><code> const DarkModeWrapper = tw.div``;
 사용방법..
 &lt;/DarkModeWrapper&gt; className={`${isDark &amp;&amp; &quot;dark&quot;}`}&gt;
     ...
 &lt;/DarkModeWrapper&gt;</code></pre></li>
</ol>
<p>2.document에 직접 접근하여, root의 클래스네임 조작하기</p>
<pre><code>   const root = window.document.documentElement
   제거..
   root.classList.remove(&quot;dark&quot;)
   추가..
   root.classList.add(&quot;dark&quot;)</code></pre><h2 id="사용방법">사용방법</h2>
<p>twin.macro의 CSS-IN-JS 방식은 크게 2가지 방식이 있습니다,
배열 형식으로 값을 입력하는것과, 기존 styled-componets의 방식과 유사한 백틱 방식인데, 두가지 방식으로 기존 darkmode의 toggle UI를 적용해보겠습니다.</p>
<p>(기존 toggle input)</p>
<pre><code>        &lt;input
        ....
          className={`${
            toggle ? &quot;-translate-x-6 bg-white &quot; : &quot;translate-x-0 bg-blue-400 &quot;
          }focus:outline-none w-6 h-6 rounded-full absolute  ....`}
    ....
        /&gt;</code></pre><p>=&gt; twin.macro 사용</p>
<p>(백틱 사용)</p>
<pre><code>  const ToggleInput = styled.input`
    ${({ toggle }) =&gt;
      toggle ? tw`-translate-x-6 bg-white ` : tw`translate-x-0 bg-blue-400 `};
    ${tw` 
    focus:outline-none 
    w-6 
    h-6 
    rounded-full 
    absolute 
    ...
   `}
  `;</code></pre><p>(배열 형식 사용)</p>
<pre><code>  const ToggleInput = styled.input(({ toggle }) =&gt; [
    toggle ? tw`-translate-x-6 bg-white ` : tw`translate-x-0 bg-blue-400 `,
    tw`
    focus:outline-none
    w-6
    h-6
    rounded-full
    absolute
    ...
  `,
  ]);</code></pre><p><code>두 방법 모두 기존 styled-componet css 문법도 추가할 수 있습니다.</code></p>
<p><a href="https://github.com/jun-05/tailwind-app/blob/layout/src/components/Toggle.jsx">사용 git repo</a></p>
<p>방법 자체는 이전에 사용하였던 tailwind-styled-components와 크게 다르지 않습니다.</p>
<p>하지만 백틱(``)을 사용하는 경우 tw을 사용하기위해선 ${}을 해주어야 하고, 배열 형식의 경우에는 바로 사용 해도 된다는 차이가 있습니다.</p>
<h2 id="nextjsts-에서-사용하기">NextJS(TS) 에서 사용하기</h2>
<p><a href="https://github.com/ben-rogerson/twin.examples/tree/master/next-styled-components-typescript">공식문서</a></p>
<p>Next에서 twin.macro를 사용하는것은 기존의 방법과 크게 다르지는 않습니다. 때문에 CRA에서의 설치법과 유사하게 진행해주시면 됩니다.</p>
<p>하지만 몇가지 추가적인 설치와 설정을 해주어야합니다.</p>
<ol>
<li>create next app은 babel-plugin-macros이 깔려있지 않습니다. 때문에 추가적으로 dev설정으로 설치해주어야 합니다.</li>
</ol>
<p>-&gt; 설치 후, 기존과 같이 babel-plugin-macros.config.js 파일 또는 package.json에 preset 설정을 해주어야 합니다.
2. TS와 같이 사용하기 위해선 @types/styled-components를 dev 설정으로 설치가 필요합니다.
3. .babelrc.js 파일을 생성하여 아래와 같은 설정을 추가하여야 합니다.</p>
<pre><code>  // In .babelrc.js
  module.exports = {
    presets: [[&#39;next/babel&#39;, { &#39;preset-react&#39;: { runtime: &#39;automatic&#39; } }]],
    plugins: [
      &#39;babel-plugin-macros&#39;,
      [&#39;babel-plugin-styled-components&#39;, { ssr: true }],
    ],
  }</code></pre><ol start="4">
<li><p>SSR을 원활하게 진행하기 위해서 pages 폴더 하위에 _document.tsx 파일을 작성하여야 합니다.
<a href="https://github.com/ben-rogerson/twin.examples/tree/master/next-styled-components-typescript#add-the-server-stylesheet">_document.tsx 작성링크</a></p>
</li>
<li><p>tailwind.config.js에서 환경에 맞게 content를 설정해주어야 합니다. 
view 페이지를 pages에서 사용한다면 ,<code>&quot;./pages/**/*.{js,ts,jsx,tsx}&quot;,</code> 
src/pages에서 사용한다면 <code>content: [&quot;./src/**/*.{js,ts,jsx,tsx}&quot;],</code> 와 같이 설정합니다.</p>
</li>
</ol>
<p>위와 같은 설정을 마치면 nextJS에서도 정상적으로 실행이 가능합니다.</p>
<h2 id="ts환경에서의-사용방법">TS환경에서의 사용방법</h2>
<p>(TS환경에서 아래와 같은 styled를 twin.macro에서 찾을수 없다고 나오는 경우, styled를 styled-components에서 가져와 해결할수있습니다.)
<img src="https://velog.velcdn.com/images/jun_53/post/8d96c4ef-51cd-4143-92ae-52d04a236ca0/image.png" alt=""></p>
<p>이전 ToggleInput에서 TS를 적용하는것을 예시로 하겠습니다.
기존 toggle의 값을 가져왔는데, toggle의 타입을 지정하여 사용할 수 있습니다. props의 타입을 지정하는것 외에 추가적인 설정은 없습니다.</p>
<pre><code>  const ToggleInput = styled.input(({ toggle }:{ toggle: boolean }) =&gt; [
    toggle ? tw`-translate-x-6 bg-white ` : tw`translate-x-0 bg-blue-400 `,
    tw`
    focus:outline-none
    w-6
    h-6
    rounded-full
    absolute
    ...
  `,
  ]);</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Tailwind CSS] TailwindCSS 기능 및 CSS in JS]]></title>
            <link>https://velog.io/@jun_53/Tailwind-CSS-TailwindCSS-%EA%B8%B0%EB%8A%A5-%EB%B0%8F-CSS-in-JS</link>
            <guid>https://velog.io/@jun_53/Tailwind-CSS-TailwindCSS-%EA%B8%B0%EB%8A%A5-%EB%B0%8F-CSS-in-JS</guid>
            <pubDate>Sun, 27 Nov 2022 16:54:17 GMT</pubDate>
            <description><![CDATA[<h1 id="tailwindcss의-기능">TailwindCSS의 기능</h1>
<p><a href="https://tailwindcss.com/docs/utility-first">공식문서 core-concepts</a>
<a href="https://tailwindcss.com/docs/adding-custom-styles">공식문서 Adding-Custom Styles</a></p>
<p>테일윈드에서 core-conepts로 소개하는것중 몇가지로, 반응형, 다크모드, Hover등의 state에 따른 style등을 사용하여 하나의 레이아웃을 만들어보겠습니다.</p>
<p>아래 예제에서 사용한 Card 및 Toggle 컴포넌트는 tailwinduikit 사이트를 참조하여 생성하였습니다.(추가적으로 일부 width 설정 및 애니메이션을 추가 하였습니다.)
<a href="https://app.tailwinduikit.com/listing/webapp/layout/grid_card">tailwinduikit</a></p>
<h2 id="기본적인-문법">기본적인 문법</h2>
<p>시작에 앞서, 많이 사용되는 기본적인 TailwindCSS 문법에 대한 설명을 하자면.</p>
<ul>
<li>기본 표현관련<ol>
<li>width와 height는 w-number , h-number 등으로 표현합니다.</li>
<li>display:flex는 TailwindCSS에서 flex 로 표현합니다.</li>
<li>justify-content: center는 justify-center와 같이 표현합니다.</li>
<li>h-screen과 표현에서는 screen이 의미하는 바는 100 뷰포인트 입니다.</li>
<li>border-radius와 같은 옵션은 rounded를 통해 표현할수있습니다.</li>
<li>space-x 기능을 사용하여, 자식 요소들간의 x축 값을 조절할수있습니다.(space-y도 가능합니다)</li>
</ol>
</li>
<li>추가 표현관련<ol>
<li>기본으로 지원하지 않는 값을 사용하려는 경우 w-[100px]과 같이 사용할수있습니다. (컬러와 같은값도 지원합니다.)</li>
<li>기존 className:hover{background-black} 표현은 hover:bg-black과 같이 표현할수있습니다.</li>
<li>몇몇 css 셀렉터를 기본 지원합니다. first: 옵션의 경우 first-child 와 같은 효과가 있습니다. <ul>
<li>지원하지 않는 css 셀렉터를 사용하고 싶다면 [&amp;&gt;div]:ml-6 와 같이 대괄호안에 셀렉터를 지정하여 사용할수있습니다.</li>
</ul>
</li>
</ol>
</li>
<li>반응형 관련<ul>
<li>반응형은 sm:값 md:값 lg:값 과 같이 표현할수있습니다. <ul>
<li>md size(768px)이상 에서는 width가 768px이고, 그 이하에서는 width를 100%를 주려한다면 아래와 같이 작성할수있습니다. 
<code>w-full md:w-[768px]</code></li>
<li>sm,md,lg 등의 반응형 사이즈를 비롯해서 여러 환경값등은 직접 커스텀 마이징 할 수 있습니다.(최상단 커스텀 문서)</li>
</ul>
</li>
</ul>
</li>
<li>dark모드 관련<ol>
<li>dark모드를 사용하기 위해선 tailwind.config.js 파일에 <code>darkMode: &quot;class&quot;,</code> 를 추가하여야 합니다.</li>
<li>상위 엘리먼트가 className으로 dark를 가지고 있어야 하위엘리먼트의 dark모드가 실행됩니다.</li>
<li>bg-white dark:bg-gray-700 과 같이 설정한다면, 기본 백그라운드값은 화이트,다크모드일때는 회색으로 변경이 됩니다.</li>
<li>상위 엘리먼트의 className을 지정하는 방법으로, 실제 HTML의 class 네임을 추가하는 방법과, 전체 Wrapper 엘리먼트를 만들고, className을 지정하는 방법 두가지가 있습니다.</li>
</ol>
</li>
</ul>
<h2 id="layout-만들기">Layout 만들기</h2>
<p>만들어볼 Layout의 기능은 크게 3가지 입니다.</p>
<ol>
<li>header / main / footer 총 3개로 영역으로 이루어져있습니다.</li>
<li>Darkmode토글 기능을 지원합니다.</li>
<li>Grid 형식으로 카드를 보여주고, 웹의 width에 따라 반응형으로 카드의 갯수를 다르게 보여줍니다.</li>
</ol>
<p>다크모드는 HTML의 클래스를 바꾸는 형식이 아닌, 상위 Elment를 만들고 하위 컴포넌트를 구현하는 방식으로 하였습니다.</p>
<pre><code>function App() {
  const [isDark, setIsDark] = useState(false);

  const toggleDarkmode = () =&gt; {
    setIsDark((prev) =&gt; !prev);
  };

  return (
    //Darkmode Wrapper
    &lt;div className={`${isDark &amp;&amp; &quot;dark&quot;}`}&gt;
      {/* 레이아웃 wrapper */}
      &lt;div className=&quot;flex flex-col min-h-screen h-full w-screen min-w-full dark:bg-slate-800 overflow-x-hidden&quot;&gt;
        &lt;header className=&quot;relative text-center h-12 border-b shadow-lg dark:text-white&quot;&gt;
          &lt;div&gt;Header&lt;/div&gt;
          &lt;div className=&quot;flex absolute top-0 right-4&quot;&gt;
            &lt;Toggle toggleFn={toggleDarkmode} /&gt;
          &lt;/div&gt;
        &lt;/header&gt;
        {/* 레이아웃 wrapper */}
        &lt;div className=&quot;flex-grow&quot;&gt;
          &lt;main className=&quot;grid grid-cols-1  md:grid-cols-3 2xl:grid-cols-4 p-4 gap-2 gap-y-4 &quot;&gt;
            &lt;Card /&gt;
            &lt;Card /&gt;
            ...
          &lt;/main&gt;
        &lt;/div&gt;
        &lt;footer className=&quot;text-center h-12 border-t shadow-inner dark:text-white&quot;&gt;
          Footer
        &lt;/footer&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}</code></pre><p>구현한 코드는 위와 같은 형식으로 작성하였습니다.</p>
<ol>
<li><p>Dark mode만을 담당하는 Wrppaer를 만들고,
하위 엘리먼트로 header,main,footer를 담을 Wrapper를 만들었습니다.</p>
</li>
<li><p>header 엘리먼트에는 relative를 사용하여 우측에 toggle 컴포넌트를 absolute를 사용하여 만들고 darkmode를 토글하는 함수를 전달하여 사용하였습니다.</p>
</li>
<li><p>main에서 grid를 사용하였고, 기본값으로 1개를 보여주고, md(768xp)에서 3개를 보여주고, 2xl(1536px)에서는 4개를 보여주도록 하였습니다.
<code>main 영역이 나머지 전체 영역을 차지할수있도록, 상위 엘리먼트에 flex-grow를 설정하엿습니다.</code></p>
</li>
<li><p>Card컴포넌트는 기본값을 w-full (100%)로, md사이즈에서는 448px을 사용하는 md:max-w-md 설정을 사용하였습니다.</p>
</li>
</ol>
<p>(다크모드 사용 및 md사이즈 이상일때)<img src="https://velog.velcdn.com/images/jun_53/post/572f4611-e104-41a1-9c1c-693cc52419be/image.JPG" alt=""></p>
<p>(다크모드를 사용하지 않고, md사이즈 미만일때)
<img src="https://velog.velcdn.com/images/jun_53/post/77e43cde-7a61-46a0-895a-70e1ec521921/image.JPG" alt=""></p>
<h2 id="tailwind-styled-components">tailwind-styled-components</h2>
<p>기능자체는 크게 문제 없이 작성하지만, 이런식으로 작성하게 되면 className이 너무 길어져 한눈에 파악하기도 힘들고, 유지보수가 힘들어지게 될것입니다.</p>
<p>이런경우 단순히 className에 사용할 tailwind옵션들을 변수를 만들어 사용하는 방법도 있을 수 있습니다.</p>
<p>하지만, CSS-IN-JS 및 컴포넌트화를 지원하는 emotion 및 styled-components와 같은 컴포넌트를 tailwind에서도 사용할 수 있습니다.</p>
<p>이런경우 twin.macro를 사용하게 되는데, 설치 및 설정이 까다로울 수 있습니다.
때문에 이번에는 간단히 사용할 수있는 tailwind-styled-components Lib를 사용해보겠습니다.</p>
<p><a href="https://www.npmjs.com/package/tailwind-styled-components">tailwind-styled-components</a></p>
<p><code>yarn add -D tailwind-styled-components</code> 를 하여 설치합니다.</p>
<p>사용할 컴포넌트 상단에 
<code>import tw from &quot;tailwind-styled-components&quot;</code> 를 추가 합니다.</p>
<p>Wrppaer를 담당하는 엘리먼트를 tailwind-styled-components를 사용하여 컴포넌트화 할 경우 아래와 같습니다.</p>
<pre><code>const Wrapper = tw.header`
flex 
flex-col 
min-h-screen 
h-full 
w-screen 
min-w-full 
dark:bg-slate-800 
overflow-x-hidden
`;

 사용방법..
 &lt;Wrapper&gt;
 ...
 &lt;/Wrapper&gt;</code></pre><p>CSS-IN-JS가 사용되는 Darkmode Wrapper는 아래와 같이 컴포넌트화 할 수 있습니다.</p>
<pre><code>const DarkModeWrapper = tw.div`
    ${(p) =&gt; (p.dark ? &quot;dark&quot; : &quot;&quot;)}
`;
사용방법..
    &lt;DarkModeWrapper dark={isDark}&gt;
    ...
    &lt;/DarkModeWrapper&gt;</code></pre><p><a href="https://github.com/jun-05/tailwind-app/tree/layout">완성 Repo</a></p>
<h3 id="문제점">문제점</h3>
<p>tailwind-styled-components는 확실히 기존의 styled-componets와 같이 간단하고 가볍게 사용할 수 있습니다.</p>
<p>하지만 TS를 사용할경우 문제가 생겨 사용하기가 매우 번거로워집니다.</p>
<ol>
<li>TS를 사용할경우, tw.div와 같이 설정할 El의 타입을 적용할때, 타입을 직접 적어주어야 합니다.</li>
<li>가장 중요한것으로, div를 비롯해 몇몇 El의 타입이 적용이 되지 않습니다. div 타입을 명시해두어도 에러가 발생합니다. </li>
<li>2 때문에 any 타입을 명시해두어야 하는데, 이럴경우 jsx의 이벤트 자동완성이 작동하지 않습니다.(onClick과 같은.., 실제 작동은 됩니다.)</li>
</ol>
<p>이러하게 tailwind-styled-components는 JS에서는 크게 문제없이 돌아가지만, TS호환이 제대로 되지 않는 문제점들을 가지고 있습니다. any 이슈는 현재 git Issue를 통해 문제에 대해 인지하고, 곧 고쳐질것 같지만, 매 컴포넌트마다 타입을 지정하기는 매우 번거롭습니다.</p>
<p>때문에 이러한 문제점들을 보완한 twin.macro를 다음 글에서 설치 및 사용해보겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Tailwind CSS] 소개 및 설치하기 with React]]></title>
            <link>https://velog.io/@jun_53/Tailwind-CSS-%EC%86%8C%EA%B0%9C-%EB%B0%8F-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0-with-React</link>
            <guid>https://velog.io/@jun_53/Tailwind-CSS-%EC%86%8C%EA%B0%9C-%EB%B0%8F-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0-with-React</guid>
            <pubDate>Sun, 27 Nov 2022 08:44:35 GMT</pubDate>
            <description><![CDATA[<h1 id="소개">소개</h1>
<p><a href="https://tailwindcss.com/">공식문서</a></p>
<p>TailwindCSS는 CSS 프레임워크입니다.
아래와 같은 사진을 보면, TailwindCSS는 다른 CSS 프레임 워크에 비해서 만족도와 사용량이 급속 성장하고 있다는것을 알 수 있습니다.
<img src="https://velog.velcdn.com/images/jun_53/post/48fd6ba7-d80a-42a7-b530-39825ce27acd/image.JPG" alt="">
<img src="https://velog.velcdn.com/images/jun_53/post/9e6f5268-68d0-4d03-8f2e-9da0a8b2b6b9/image.JPG" alt="">
<a href="https://2021.stateofcss.com/ko-KR/technologies/css-frameworks/">출처</a>
이러한 결과를 바탕으로, 카카오등을 포함한 많은 기업들이 TailwindCSS를 도입하고 있습니다.</p>
<p>TailwindCSS의 특징으로는 아래와 같은것이 있습니다.</p>
<ul>
<li>클래스명에 직접 CSS옵션을 적용하기때문에, 클래스 이름을 크게 고민할 필요가 없습니다.</li>
<li>CSS의 많은 부분을 직관적인 추상화를 해주어, 간단하게 입력이 가능합니다. </li>
<li>필요한 부분은 커스텀마이징을 통하여 기능을 추가 할 수 있습니다.</li>
<li>실제 CSS를 사용하는것과 이름 등이 크게 다르지 않으며, 마우스를 hover 할 경우, 실제 CSS에서 어떻게 적용되는지 알 수 있습니다.</li>
<li>반응형 환경을 조금 더 쉽게 조작 할 수 있습니다.</li>
<li>VS코드를 사용할경우 자동완성 기능을 제공해주는 Tailwind CSS IntelliSense 라는 확장프로그램이 있습니다.</li>
</ul>
<h1 id="설치">설치</h1>
<p>공식문서에서 여러 환경에서의 설치방법을 소개하고있습니다.
저는 CRA를 사용한 React 템플릿에서 설치해보겠습니다.
<a href="https://tailwindcss.com/docs/guides/create-react-app">공식문서의 설치방법</a>
저는 yarn을 사용하여 진행하였습니다, 하지만 npx와npm을 사용하여도 전혀 상관없습니다.</p>
<ul>
<li><p>yarn create react-app tailwind-app</p>
</li>
<li><p>폴더로 이동후, tailwind 및 tailwind Lib를 설치합니다.</p>
</li>
<li><p>yarn add -d tailwindcss postCSS와 autoprefixer</p>
</li>
<li><p>yarn tailwindcss init -p 을 실행하게 되며 tailwind.config.js 파일이 생성됩니다.</p>
</li>
<li><p>설치된 tailwind.config.js 에서 다음과 같이 설정을 진행합니다. content 옵션은 tailwindCSS가 적용되는 범위를 설정합니다.</p>
<pre><code>/** @type {import(&#39;tailwindcss&#39;).Config} */
module.exports = {
content: [
  &quot;./src/**/*.{js,jsx,ts,tsx}&quot;,
],
theme: {
  extend: {},
},
plugins: [],
}</code></pre></li>
<li><p>index.css 파일의 내용을 모두 지우고, 아래와 같이 CSS 파일을 가져옵니다. 
<code>tailwind는 기본적으로 사용하는 nomalize가 있기때문에,별도로 reset.css 등을 적용하려 한다면 정상적으로 작동하지 않습니다.</code></p>
<pre><code>@tailwind base;
@tailwind components;
@tailwind utilities;</code></pre><ul>
<li>CRA는 웹팩 설정이 되어있기때문에, 아래와 같이 Index.css을 사용하지않고, index.js 파일에서 Import 형식으로 tailwindcss/tailwind.css를 가져올 수 있습니다.<pre><code>import React from &quot;react&quot;;
import ReactDOM from &quot;react-dom/client&quot;;
import &quot;tailwindcss/tailwind.css&quot;;
import App from &quot;./App&quot;;
const root = ReactDOM.createRoot(document.getElementById(&quot;root&quot;));
root.render(
&lt;React.StrictMode&gt;
&lt;App /&gt;
&lt;/React.StrictMode&gt;
);
</code></pre></li>
</ul>
<pre><code></code></pre></li>
</ul>
<p>여기까지 진행되었다면, 기본적인 셋팅은 끝났습니다.</p>
<p>실제 적용을 위해서 App.js의 div를 아래와 같이 설정하여 시작하여 테스트 해볼 수 있습니다.
<code>tailwind를 사용하기 위해서 HTML에서는 class를 사용하지만, JSX에서는 className을 사용합니다.</code></p>
<pre><code>function App() {
  return &lt;div className=&quot;text-3xl text-blue-500&quot;&gt;hello world&lt;/div&gt;;
}</code></pre><p>화면에 파란색의 h1크기정도의 텍스트가 나타난다면 설치는 정상적으로 완료된것입니다.</p>
<p>여기에 추가적으로, 설정의 자동완성을 도와주기 위해서 VScode의 마켓플레이스에서 Tailwind CSS IntelliSense를 검색하여 설치합니다.
이 확장 프로그램은 tailwind에서 사용하는 옵션들을 자동완성 시켜줍니다.</p>
<p><img src="https://velog.velcdn.com/images/jun_53/post/701b4376-139c-417b-a84e-4824ebfd94a2/image.jpg" alt=""></p>
<p>그리고 다시 한번 App.js에서 텍스트의 색을 red로 바꾸려 한다면, 다음과 같이 자동완성이 나타나는것을 볼 수 있습니다. </p>
<p><img src="https://velog.velcdn.com/images/jun_53/post/09ac3403-163f-41da-8c2e-bc285a34859f/image.png" alt=""></p>
<p>또한 이 확장도구를 사용하면, tailwind에서 사용된 옵션들에 마우스를 위치시키면 실제 CSS 속성이 어떻게 적용되어 있는지도 알 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WANTED] 프리온보딩 코스 week 3-2]]></title>
            <link>https://velog.io/@jun_53/WANTED-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%BD%94%EC%8A%A4-week-3-2</link>
            <guid>https://velog.io/@jun_53/WANTED-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%BD%94%EC%8A%A4-week-3-2</guid>
            <pubDate>Sat, 26 Nov 2022 20:34:34 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가기">들어가기</h1>
<p>이번강의에서는 이전 과제에 대한 피드백과, 
Redux, React-Query에 대해서 다루었습니다.</p>
<p>(해당 게시글은 실제 강의 내용을 그대로 옮겨적은것은 아니며, 주관적인 이해 및 판단으로 요약되어진 글입니다.)</p>
<h1 id="강의-section_1">강의 Section_1</h1>
<h2 id="과제-피드백">과제 피드백</h2>
<p>이전과 같은 형식으로 진행되었습니다.
공통적인 사항으로서, 이전까지의 대부분의 팀들은 CRA를 사용하고 있었는데, 꼭 CRA를 사용하기보단, Vite를 사용하여 React project를 만드는것도 좋을 수 있다고 권장해주셨습니다.</p>
<p>저번 과제의 주제이기도 하였고, 이번 강의에서의 주제이기도 한 캐시를 주제로 많이 다루었는데, 캐시 정책에 관한것이 기억에 남았습니다.</p>
<p>일반적으로 캐시 가능한 응답을 요청 받았을 경우 웹 브라우저의 캐시를 찾아보게 되는데, 만약 유효시간(max-age)내에 있는 캐시가 있다면 네트워크 요청없이 바로 응답을 보낼 수 있게됩니다.</p>
<p>이러한 설정은 네트워크의 Header 태그에서 많은 부분을 살펴 볼 수 있습니다.
<img src="https://velog.velcdn.com/images/jun_53/post/c2f88bb1-d466-437c-8a7a-d798f4b33a8e/image.JPG" alt=""></p>
<p>하지만 유효시간이 지나고 요청이 들어올경우에는, 캐시 정책(cache-contorl)과 검증헤더 (date),etag를 사용하여 요청을 어떻게 할지 결정하게 됩니다.</p>
<p><code>cache-contorl의 private / public은 프록시 서버 (CDN)에 저장해도 괜찮은지 여부 입니다. 위와 같은 설정일 경우 브라우저 캐시에만 저장되게 됩니다.
max-age=0인 경우는 캐시가 즉각적으로 만료됨을 의미합니다.
must-revalidate는 캐시 만료 후 최초 조회시 원 서버에 검증하는 설정이며, 캐시 유효 시간이라면 캐시를 사용합니다.</code></p>
<p>우선 클라이언트와 서버측의 date, 마지막 수정시간을 비교하여, 데이터의 신선도(state)을 체크 하게 됩니다. </p>
<ul>
<li>만약 설정된 시간조건에 부합하다면 304를 응답하여서 Header만 리턴하고, 브라우저는 304 응답을 확인하고 기존의 브라우저 캐시를 갱신하고, 갱신된 캐시값을 사용합니다.</li>
<li>설정된 시간조건에 부합하지 않는다면 새로운 컨텐츠를 받아오고 200 응답을 받아옵니다.</li>
</ul>
<p>두번째로, Etag(Entity Tag)는 컨텐츠가 가지는 고유값으로서, ID역할을 수행합니다.</p>
<ul>
<li>요청이 들어왔을때 서버측 Etag와 일치하다면 304 요청과 브라우저는 기존 캐시값을 재사용합니다.</li>
<li>Etag값이 일치하지 않는다면 컨텐츠가 변경되었다는 의미이므로, 새로운 컨텐츠를 받아오고 200 응답을 받아옵니다.</li>
</ul>
<p>여기까지, 직접적인 과제 피드백은 아니였지만 이전 과제가 캐시와 관련된 주제인 만큼. 이러한 주제를 짚고 넘어갔습니다.</p>
<p>기억에 남는 과제 피드백은 아래와 같았습니다.</p>
<ol>
<li><p>저번 과제에서 검색어는 검색결과 리스트에 BOLD 처리를 하는게 요구기능중 하나였는데, 영문자일 경우 소문자만 처리하는 경우가 많았습니다. 하지만 이러한 경우에도 대소문자 상관없이 BOLD 처리를 해주는게 좋지 않았을까 하는 조언이 있었습니다.</p>
</li>
<li><p>캐시 데이터의 expiredTime을 지정하고, 캐시 값을 불러오거나 사용할때, 현재 시간과 비교하여 사용하는게 좋다는 조언이 있었습니다.</p>
</li>
<li><p>import를 정리하는 방법으로서
외부 의존성 / 내부 의존성(멀리서 가져오는) / 내부 의존성(가까이서 가져오는) 종류에 따라 줄바꿈을 하는 편이 좋을 수 있다는 조언이 있었습니다.</p>
</li>
<li><p>데이터를 처리할때, 성능 측정에 따라 사용하는 함수가 달라질수있는데, 이러한것은 근거로서 명시하는게 좋다고 조언하셨습니다.</p>
</li>
</ol>
<p>이번 과제와 같은경우, 캐시데이터를 Obj 형식보다는, HashMap을 사용하는 경우 더 나은 성능을 보여서 채택한 팀이 있었습니다.</p>
<p>이러한 성능 테스트는 
<code>console.time(&quot;test&quot;)</code>
<code>console.timeEnd(&quot;test&quot;)</code>
를 사용하여 테스트 해볼수있습니다.</p>
<p>다른팀의 과제중 좋았다고 생각한 코드는 아래와 같았습니다.</p>
<pre><code>export class CacheService&lt;K, V&gt; {
  private state: Map&lt;K, V&gt; = new Map();

  setCache(key: K, value: V) {
    this.state.set(key, value);
  }

  getCache(key: K) {
    return this.state.get(key);
  }

  hasCache(key: K) {
    return this.state.has(key);
  }
}</code></pre><p> (출처 : 수업 자료)</p>
<p>제거와 유효시간에 관한 기능은 없었지만, 위와 같은 구조로 코드를 짠다면 클린코드에 가깝게 구현할 수 있다는 생각이 들었습니다.</p>
<h1 id="강의-section_2">강의 Section_2</h1>
<p>본 강의에서 주 주제는 리액트 쿼리였지만, 리덕스에 대해서 짧게 설명을 가졌습니다.</p>
<h2 id="리덕스">리덕스</h2>
<h3 id="리덕스란">리덕스란?</h3>
<p>자바스크립트는 싱글 페이지 애플리케이션이 갖추어야 할 요건이 점점 복잡해지며, 더 많은 상태(state)를 관리할 필요가 생겨났습니다. state가 많아지면 많아질수록, 프로그래머는 어플리케이션에서 state를 통제하기 어려워지는 상황을 맞닥뜨리게 됩니다. 
이러한 요건 속에서 리덕스는 데이터를 통제하기 위해 고안된 Flux(단방향 데이터 흐름) 패턴 기반의 구현체입니다.</p>
<p><img src="https://velog.velcdn.com/images/jun_53/post/362d50d1-14e0-4842-bd62-659e2a7dd542/image.png" alt=""></p>
<p>리덕스는 위와 같은 흐름으로 작동됩니다.</p>
<p>우선적으로, Plain 자바스크립트 객체인 액션함수가 있어야하며, 디스패쳐를 통해 액션 함수를 실행합니다.
이 액션함수를 통해 <strong>순수 함수</strong>인 Reducer 내에서 데이터의 변경이 일어나고, 이 데이터는 Store에 저장되어 전역적으로 사용되어집니다.</p>
<p><code>순수 함수란, 동일한 값이 들어오면 항상 같은 값을 리턴하는 함수입니다.(외부 상태에 영향을 주지 않는 함수)</code></p>
<p>리덕스에서 스스로 소개하는 3가지 원칙은 다음과 같습니다.</p>
<ol>
<li>진실은 하나의 근원으로부터<ul>
<li>애플리케이션의 모든 상태(state)는 하나의 저장소(store) 안에 하나의 객체 트리 구조로 저장되어야한다</li>
</ul>
</li>
<li>상태(state)는 읽기 전용이다<ul>
<li>상태를 변화시키는 유일한 방법은 액션 객체를 전달하는 방법뿐입니다.</li>
</ul>
</li>
<li>변화는 순수 함수로 작성되어야 한다.<ul>
<li>액션에 의해 상태 트리가 어떻게 변화하는것을 지정하는 리듀서 함수를, 순수 함수로서 작성해야 합니다.</li>
</ul>
</li>
</ol>
<p>리듀서의 컨셉은 아래와 같습니다.</p>
<ol>
<li>리덕스 함수는 reducer를 인자로 받습니다.</li>
<li>클로저로 state와 listeners 를 갖습니다.</li>
<li>state를 그대로 반환하는 getState 함수를 하나 가지고 있습니다.</li>
<li>subscribe라는 함수에서 listnerer를 인자로 받아, 기존 클로저의 listeners 배열에 넣습니다. 받은 listnerer는 dispatch가 실행 될때마다 실행됩니다. 리턴값으로 unsubscribe 를 반환하여, 등록을 해제 할수있도록 해줍니다.</li>
<li>dispatch 함수는 plain 객체인 action을 받아, 처음 생성시 전달받은 reducer에 기존 state와 action을 전달하여 state를 변경시킵니다.</li>
<li>3,4,5 함수를 생성하여, 객체 리터럴로 return하여 반환합니다.</li>
</ol>
<p>4번같은 경우 React Redux를 통해 <code>&lt;Provider store={store}&gt;</code> 와 같은형식으로 뷰 바인딩을 하게 됩니다.</p>
<p>이러한 리덕스를 사용하게 된다면, 전역 상태관리를 보다 손 쉽게 할수있게되고, 멀리 떨어진 컴포넌트 간의 통신을 필요한 값만을 전달할수있게 해주어, props drilling을 막을 수도있게 됩니다.  </p>
<h2 id="react-query">React Query</h2>
<h3 id="리덕스의-문제점">리덕스의 문제점</h3>
<p> 리덕스는 확실히 비동기 통신에서의 데이터를 store에 보관하는데 많이 사용됩니다. 하지만 여기서 의문이 하나 발생할 수 있습니다. store에 보관된 값이 정말 현재 DB에 저장되어있는 값이라고 볼 수 있을까?</p>
<p>클라이언트에서 가져와 쓰는 순간부터 이미 그 데이터는 정말 진실한 데이터가 아닐 위험이 존재하지 않을 수 있을까?</p>
<h3 id="state-while-revalidate">state-while-revalidate</h3>
<p>이러한 문제점을 개선하기위해, 리액트 쿼리는 state-while-revalidate라는 전략을 가지고 왔습니다.</p>
<p>이것은 두 가지 프로세스로 분리할 수 있는데.
요청이 들어왔을경우.</p>
<ol>
<li>Cache-Control Header의 max-age를 확인하여, max-age값이 아직 유효하다면 기존 값을 그대로 사용하기때문에 아무것도 하지 않습니다. 하지만, max-age값이 넘어갈 경우 두번째 스텝으로 넘어갑니다.</li>
<li>stale-while-revalidate 값을 확인하여 두가지 경우의 수를 가집니다.
2-1. stale-while-revalidate 값을 넘지 않았다면 우선 아직 캐싱된 값을 사용합니다. 하지만 동시에 캐시된 응답의 사용을 지연시키지 않는 방식으로, 데이터에 대한 재검증 요청이 이루어집니다. 이 값은 기존에 캐시된 항목을 대체하고, max-age값에 비교되는 타이머를 재설정합니다.
2-2 만약 값이 넘었다면 캐시값을 사용하지 않고, 데이터를 새로 요청해서 최신화 합니다.</li>
</ol>
<p>이러한 전략으로 리액트 쿼리는 데이터에 대한 진실한 데이터를 지키기 위해서 노력합니다.</p>
<h3 id="react-query-1">React Query</h3>
<p>우선, 리액트 쿼리는 stale-while-revalidate 값을 staleTime 이라는 값으로 설정합니다. 
  이 값은 개별적인 reactQuery 함수에 설정할수도, 전체적인 설정을 가지는 queryClient에서도 설정할 수 있습니다.</p>
<p>리액트 쿼리는 상당히 많은 기능들이 추상화 되어있습니다.
대표적인것을 꼽자면</p>
<ol>
<li>리액트 쿼리의 작동방식</li>
<li>리액트 쿼리의 비동기 호출의 과정 자동화가 있습니다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/jun_53/post/73775b43-dff2-4a2f-96e5-8b162cbe91cf/image.jpg" alt="">
  <a href="https://dev.to/rootstrap/react-query-and-management-of-server-state-8ol">이미지 출처</a>
1번에 대한 설명으로서, 리액트 쿼리는 위 이미지에서 보이는것과 같은 state를 가집니다.
  2가지 관점으로 보여질 수 있을것 같습니다. 
     데이터를 받아 stale에서 active상태를 유지하는것과, 더 이상 사용되지 않는 Inactive상태 이렇게 두가지로 나뉘어질 수 있을것 같습니다.
  우선, active에 대한 설명으로는 아래와 같습니다.</p>
<ul>
<li><p>Fetching은 서버에서 데이터를 가져오는 것을 의미합니다.</p>
</li>
<li><p>Fresh는, 데이터를 막 받아온 상태이기때문에, 서버/클라이언트의 정보가 동일하다는것이 보장됩니다. 하지만 서버는 항상 데이터를 주고받기 때문에, 시간이 조금이라도 지나면 데이터의 동일성은 보장받기 어렵습니다. 때문에 react-query에서는 Fresh에서 stale 상태로 넘기는 옵션인 staleTiem의 기본값을 0(즉시)로 설정되어져있습니다.</p>
</li>
<li><p>Stale은 서버/클라이언트의 정보가 동일함을 보장할 수 없는(신선하지 않은) 상태입니다. 서버로부터 새로운 값을 받지 않았다면 Stale하다고 할 수 있습니다. 이경우 React Query는 값을 업데이트 하기 위해 새요청을 보냅니다.</p>
<p>요청을 주고받는경우, 위의 3가지 상태가 계속해서 진행됩니다. 하지만  Stale에서 더 이상 사용되지 않는경우, Inactive상태로 변환됩니다.</p>
</li>
<li><p>Inactive는 해당 쿼리가 React Query 가비지 컬렉터에 의해 제거될 예정임을 알립니다.</p>
</li>
<li><p>Deleted는 삭제된 쿼리를 의미합니다.</p>
<p>여기까지가 React Query의 기본적인 작동 개념이라고 볼 수 있을것 같습니다.</p>
<p>이어서, 2번에 대한 설명으로는, 정말 많은 return값과 query option등이 있습니다.</p>
<pre><code>const {
data,
error,
isError,
isLoading,
status,
...
} = useQuery({
queryKey,
queryFn,
cacheTime,
enabled,
onError,
onSettled,
onSuccess,
select,
staleTime,
useErrorBoundary,
...
})</code></pre><p><a href="https://tanstack.com/query/v4/docs/reference/useQuery">공식 Dev API출처</a>
더 많은 값들을 받아 올 수있지만, 대표적으로는 저런것 등이 있습니다.
앞의 값은 객체 리터럴로 return된 값이고, 뒤는 리액트 쿼리 함수의 옵션입니다.</p>
<p>data,isLoading 등은 기존 reducer등을 사용하여 많이 사용하였습니다.
또한 onSuccess, onError등, 기존 middlewere에서 처리하던것들을 React-qeury는 단지 하나의 함수내에서 모두 처리 할 수 있도록 도와줍니다.
기존 redux-thunk 또는 saga등을 통하여 server state를 처리하던것을 React-query는 레이어 전체를 추상화 시켜 개발자가 앱 내에서 서버 상태를 제외한 UI 상태에 더욱 집중 하여 개발 할 수 있도록 도와주었습니다.</p>
<h3 id="react-query-사용방법">React Query 사용방법</h3>
<p>React Query의 대표적인 함수로는 3가지가 있습니다.</p>
</li>
</ul>
<ul>
<li><p>useMutaion : 데이터의 추가 및 수정등에 사용되어집니다.</p>
</li>
<li><p>useQuery : 데이터의 조회에 사용되어집니다.</p>
</li>
<li><p>queryClient.invalidateQueries : 전체 또는 특정 쿼리를 stale취급하고, 현재 사용되어지고 있는 query들은 백그라운드에서 refetch 시키게 됩니다. useMutaion과 같이, 값이 수정되는 작업 이후(onSuccess) 사용하여 쿼리를 stale상태로 옮겨 refetch 하는것을 도와줍니다.</p>
<p>가장 많이 사용되어지는 useQuery에 대한것을 설명하자면,
useQuery에 필수적인 props로 2가지를 필요로 합니다, 하나는 고유 키값, 하나는 fetch 함수입니다.
<code>키값은 반드시 배열로서 들어가야 합니다.</code></p>
<pre><code class="language-tsx">const [id, setId] = useState(1)
</code></pre>
</li>
</ul>
<p>const { data } = useQuery([&#39;item&#39;, id], () =&gt; fetchItem({ id }))</p>
<p>&lt;button onClick={() =&gt; {
  // ✅ set id without explicitly refetching
  setId(2)
}})&gt;Show Item 2</button></p>
<pre><code>   (출처 : 수업 자료)

  위와 같이, useQuery 함수는 첫번재 인자로 고유 키값 [&#39;item&#39;, id]을 받고, 두번째로는 fetch 함수를 사용합니다. 이후 3번째 인자부터는 옵션이기 때문에 꼭 필요하지는 않습니다.

  `useQuery에서 응답된 data들은 고유 키값에 보관되어있고, 또한 캐시에 존재하는 응답값에 접근하여 값을 읽어 올수도, 업데이트 할 수도 있습니다. 때문에 useQuery는 전역 상태로도 생각 할수 있습니다.`

이러한 키값들이 사용되는 경우가 많다면, 이러한것을 아래와 같이 묶음화해서 관리하는것도 좋은 방법일 수 있습니다.
</code></pre><p>  [&#39;todos&#39;, &#39;list&#39;, { filters: &#39;all&#39; }]
[&#39;todos&#39;, &#39;list&#39;, { filters: &#39;done&#39; }]
[&#39;todos&#39;, &#39;detail&#39;, 1]
[&#39;todos&#39;, &#39;detail&#39;, 2]</p>
<pre><code>  =&gt; todoKeys 객체에서 함수를 통해 키값을 관리</code></pre><p>  const todoKeys = {
  all: [&#39;todos&#39;] as const,
  lists: () =&gt; [...todoKeys.all, &#39;list&#39;] as const,
  list: (filters: string) =&gt; [...todoKeys.lists(), { filters }] as const,
  details: () =&gt; [...todoKeys.all, &#39;detail&#39;] as const,
  detail: (id: number) =&gt; [...todoKeys.details(), id] as const,
}</p>
<pre><code>  (출처 : 수업 자료)

  ### React-Query를 커스텀 훅을 사용하여 관리하기

  리액트 쿼리를 사용 view를 담당하는 컴포넌트에서 사용하는것은 별로 좋은 판단이 아닐것입니다. 기존 커스텀 훅을 사용한것처럼, 리액트 쿼리도 커스텀 훅을 사용하여, 필요한곳에서 적재적소에 사용하는것이 코드관리에 좋을것입니다.
</code></pre><p>export const useTodos = () =&gt; {
  const client = useQueryClient();</p>
<p>  const { data, ...queryResult } = useQuery(&quot;todos&quot;, todoRepository.getTodos);</p>
<p>  const createTodo = useMutation(todoRepository.createTodo, {
    onSuccess: () =&gt; client.invalidateQueries(&quot;todos&quot;),
  }).mutate;</p>
<p>  const updateTodo = () =&gt; {
        // ...
    };</p>
<p>  const deleteTodo = useMutation(todoRepository.deleteTodo, {
    onSuccess: (data, id) =&gt; client.invalidateQueries(&quot;todos&quot;),
  }).mutate;</p>
<p>  return {
    ...queryResult,
    todos: data,
    createTodo,
    updateTodo,
    deleteTodo,
  };
};</p>
<pre><code>   (출처 : 수업 자료)
  - todoRepository는 todo 관련 api통신을 하는 class입니다.
 - useMutation 함수는 키값이 필요하지 않고, fetch 함수를 필수 인자로 합니다
  - 성공 후, invalidateQueries 함수를 실행하여 기존 캐시값의 todos 값들을 stale로 옮겨 refetch 시킬 수 있도록 합니다. 

  위와 같이, 커스텀 함수를 사용하여 관리한다면 기존 redux를 사용하여 서버 상태관리를 했을때와 비교했을때 훨씬 더 간결하고 응집도가 높게 관리할수있습니다.

  ### co-location(함께 위치시키기) 방식의 파일구조.
 리액트 쿼리의 기능과 직접적인 관련이 있는것은 아니지만,
  리액트 쿼리의 메인터너는 쿼리 파일을 기능 파일에서 떨어진곳에 위치하거나, 한곳에 위치하는것 보다는, 기능 디렉터리에 함께 보관하는것을 제안하였습니다.
</code></pre><ul>
<li>src</li>
<li>features<ul>
<li>Profile<ul>
<li>index.tsx</li>
<li>queries.ts</li>
</ul>
</li>
<li>Todos<ul>
<li>index.tsx</li>
<li>queries.ts<pre><code></code></pre></li>
</ul>
</li>
</ul>
</li>
</ul>
<p>이러한 형식으로 코드구조를 짜게 된다면, queries 파일 뿐만아니라, api,hooks 및 기타 필요한 파일 및 폴더 등이 모두 features의 각 feature 항목에 위치시키게 될것입니다.</p>
<h1 id="마무리-소감">마무리 소감</h1>
<p>사실, 글 올리는 시점 기준으로, 수업 자체는 거의 2주전이였습니다.
그 사이에 1주일 짜리 과제가 끝나고, 전체적인 과정이 끝난 1주일간은 밀린 일들을 처리하고, 밀려있던 게으름 등.. 여러 이유를 핑계로 글 쓰는게 늦어진것 같습니다.</p>
<p>이번 강의에서는 header에서 캐시를 살펴보는 방법에 대해서 알아보았던점이 좋았고,
  react query를 사용하기이전에는 거의 항상 사용하던 redux의 개념 및 컨셉, 3가지원칙등에 대해서 자세히 알아보게 되어 좋았습니다.
  그래도 프로젝트가 끝난 시점에서 말하는거지만, 가장 좋았던건 react-query에 대해서 알게 된점인것 같습니다.
  실제로 사용해보니 마법 같은 Lib였던것 같습니다.
  기존에 번거로웠던 loading 및 error 처리등을 리액트 쿼리를 사용한 fetch 요청 한번에 모두 객체 리턴값으로 받아 사용 할 수있고, 성공시 method를 추가 할수있는점, 자동으로 캐시를 사용하여 관리해준다는점. 그리고 활용하는 방법으로서의 커스텀훅 방법 등.. 개념적으로도, 실무적인 점으로도 많은 점을 배울 수 있어 좋았습니다.
 하지만, 이번 강의를 들으면서 한편으로는, 단순히 마법같은 lib를 그저 받아들이는게 아니라, 실제로 파헤쳐보고 이해해봐야 하는 필요성을 느낄 수 있었습니다.
  아마, 지금은 아니겠지만.. 언젠간 react를 비롯해서 자주 사용하는 Lib등을 모두 소스를 파헤쳐보고 싶어졌습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WANTED] ASSIGNMENT_5]]></title>
            <link>https://velog.io/@jun_53/WANTED-ASSIGNMENT5</link>
            <guid>https://velog.io/@jun_53/WANTED-ASSIGNMENT5</guid>
            <pubDate>Sat, 12 Nov 2022 13:21:15 GMT</pubDate>
            <description><![CDATA[<h1 id="assignment_5">ASSIGNMENT_5</h1>
<h2 id="다섯번째-과제의-요구사항">다섯번째 과제의 요구사항</h2>
<p>과제의 대략적인 요구사항은 아래와 같았습니다.</p>
<ol>
<li>대략적인 Page 디자인이 주어졌고, 그것에 따라 UI 디자인</li>
<li>인풋창에 값 입력시 리턴 값들을 List로 보여줄것</li>
<li>API요청을 매 입력시마다 하지 않도록 할것</li>
<li>캐싱 기능이 있는 Lib를 사용하지 않고, 같은 URL로 API 요청시, 실제 요청은 보내지 않고, 캐싱된 값을 보여줄것</li>
<li>List에 나타난 값들중 인풋값과 같은 글자는 Bold 처리 할것</li>
<li>List값들은 키보드 이벤트로 상호작용 할 수 있도록 할것</li>
</ol>
<p>++ API 서버는 별도로 주어지지 않으며, json 파일만을 가진 repo를 json 서버로 구동하여 로컬환경에서 작업할것</p>
<h2 id="시작하기에-앞서">시작하기에 앞서</h2>
<h3 id="초기-셋팅">초기 셋팅</h3>
<p>초기 셋팅은 이전 과제물들과 동일하게 진행하였습니다.
불필요한 파일이 제거된 CRA를 생성하고,
ESLint,Prettier,husky가 설정이 된 repo를 main으로 올린 뒤, 팀원들이 모두 클론하여 각자의 브런치를 만들고 진행하였습니다.</p>
<p>추가적으로, 저는 이번 프로젝트에서 TS를 사용하였기 때문에, 우선 CRA를 클론한 후, typescript CRA를 따로 생성하여, 기존 파일에 덮어 쓰기하여 파일을 변경하여 git을 관리하였습니다.</p>
<h2 id="코딩구현">코딩구현</h2>
<h3 id="api-요출-횟수-제한-및-캐싱기능">API 요출 횟수 제한 및 캐싱기능</h3>
<p>debounce와 캐시 스토리지를 사용하였습니다.</p>
<p>debounce는 seTimeout을 통해서 일정횟수에 한번씩만 동작하게 하는 함수입니다.
캐시 스토리지는 로컬스토리지와 비슷하게 작동하지만, URL을 파라미터로 받아, 만약 같은 URL로의 요청일시 저장된 캐싱값을 사용하는 브라우저의 내장 기능-함수입니다.</p>
<p><a href="https://usehooks.com/useDebounce/">useDebounce 참고 사이트</a>
<a href="https://www.geeksforgeeks.org/how-to-store-multiple-cache-data-in-reactjs/amp/">캐시 스토리지 참고사이트</a></p>
<p>두 사이트를 통해 얻게된 지식으로 기능구현은 크게 문제 없었습니다.</p>
<pre><code> const debouncedSearchTerm = useDebounce(inputValue, 300);
  useEffect(() =&gt; {
    const getSick = async () =&gt; {
      if (isBlankVal(debouncedSearchTerm)) {
        return;
      }
      const SearchURL = `URL...`;
      const cacheData = await getSingleCacheData(debouncedSearchTerm.trim(), SearchURL);
      if (cacheData.length &gt; 0) {
        setSickData(cacheData);
      } else {
        const response = await SearchService.getSick(debouncedSearchTerm.trim());
        setSickData(response.data);
        addDataIntoCache(debouncedSearchTerm.trim(), SearchURL, response.data);
        console.info(&#39;calling api&#39;);
      }
    };
    if (debouncedSearchTerm) {
      getSick();
    } else {
      setSickData([]);
    }
  }, [SearchService, debouncedSearchTerm]);
</code></pre><ol>
<li>useState를 통해 form의 인풋값인 inputValue 를 관리하였고,이것을 useDebounce를 통해 이 값이 0.3초에 한번씩만 리턴되도록 하였습니다.</li>
<li>service는 변경되지 않는 값이며, input값이 변경될시 debounce값도 변경되기에, 인풋값이 변경될때만 해당 useEffect로직이 실행됩니다.</li>
<li>전체적으로 공백값일시 검색되지 않도록 햇으며, trim을 사용해 앞에 공백이 있어도 작동하도록 하였습니다.</li>
<li>디바운스 값이 있을시에만 로직이 실행되었으며, 만약 &#39;&#39;과 같은 falsy값일시 빈 배열을 리턴하도록 하였습니다.</li>
<li>실제 요청 이전에 캐시값이 있으면 요청값 대신 기존 저장된 값을 데이터로 사용하도록 하였습니다.</li>
</ol>
<h3 id="검색값과-같은-list결과-bold처리">검색값과 같은 List결과 Bold처리</h3>
<p>replaceALl을 사용하여 검색값과 같은 부분은 모두 strong 태그로 감싼 텍스트를 생성하였고, 이후 이 값을 react JSX의 dangerouslySetInnerHTML 기능을 사용하여 텍스트가 태그가 있으면 그대로 반영되도록 하였습니다.</p>
<p>하지만 dangerouslySetInnerHTML의 사용은 지양하는것이 좋기 때문에, regex를 사용하여 배열을 만들고, 그 배열을 map을 돌려 처리하는것이 더 옳은 방법이였다고 생각합니다.</p>
<pre><code>export function parseTextBold(originText: string, targetText: string) {
  const text = originText.replaceAll(targetText, &#39;&lt;strong&gt;&#39; + targetText + &#39;&lt;/strong&gt;&#39;);
  return text;
}

   &lt;div dangerouslySetInnerHTML={{ __html: parseTextBold(sickName, inputValue) }}&gt;&lt;/div&gt;</code></pre><p>=&gt; 구현과 사용</p>
<h3 id="form에서-키보드-이벤트를-통해-드랍다운-상호작용">Form에서 키보드 이벤트를 통해 드랍다운 상호작용</h3>
<p>Input이 Focus 되어있는 상황에서, 키보드 이벤트를 통해 렌더링된 List에 UI상의 변화를 주는 요구사항이였습니다.</p>
<p>기능구현은 커스텀훅을 사용하여, Form을 감싸는 container에서 키보드 이벤트를 주었습니다.</p>
<p>대략적인 기능 구상은 아래와 같습니다.</p>
<ul>
<li>input창에서 키보드 이벤트 실행</li>
<li>키 타입이 ArrowUp 또는 down이면 selectNum이라는 state를 통해 몇번째 List가 활성화 되어야 하는지 정한다.</li>
<li>맨끝 또는, 맨위에서 한칸 더 이동하는 경우에 대한 예외처리 정리(state값이 처음 또는 맨 아래로 향하게 한다)</li>
</ul>
<p>추가적으로, 해당 리스트 값을 클릭시 마찬가지로 Num값에 대한 state를 변경해주도록 하였습니다. 
이러한 경우 Div가 클릭되면 input창으로부터 focus가 벗어나게되어 keyboard 이벤트가 발생되지 않는데, forward Ref를 통해 ref를 넘겨주어도 괜찮은 방법이지만, List를 감싸주는 컴포넌트에 hidden input을 만들어 그곳에 focus를 주는 방법을 택하였습니다.</p>
<pre><code>  const onKeyEventHanler = (e: React.KeyboardEvent&lt;HTMLTextAreaElement&gt;) =&gt; {
    if (e.nativeEvent.isComposing) {
      return;
    }
    const typing = e.code;
    upDownValHandle(typing, sickData);
  };

  export function useSelectVal() {
  const [selected, setSelected] = useState(&#39;&#39;);
  const [selectNum, setSelectNum] = useState(0);
  const handleSelected = (value: string, idx?: number) =&gt; {
    setSelected(value);
    if (idx !== undefined) {
      setSelectNum(idx + 1);
    }
  };

  const upDownValHandle = (typing: string, sickData: sick[]) =&gt; {
    switch (typing) {
      case &#39;ArrowDown&#39;:
        if (sickData.length &lt;= selectNum) {
          setSelectNum(1);
          const selectData = sickData[0];
          handleSelected(selectData?.sickNm);
        } else {
          setSelectNum(prev =&gt; prev+1);
          const selectData = sickData[selectNum];
          handleSelected(selectData?.sickNm);
        }
        break;
      case &#39;ArrowUp&#39;:
        if (selectNum &lt;= 1) {
          setSelectNum(sickData.length);
          const selectData = sickData[sickData.length - 1];
          handleSelected(selectData?.sickNm);
        } else {
          setSelectNum(prev =&gt; prev-1);
          const selectData = sickData[selectNum - 2];
          handleSelected(selectData?.sickNm);
        }
        break;
      default:
        break;
    }
  };</code></pre><p>==&gt; selected는 선택된 리스트이 텍스트값입니다. 
==&gt; <code>const selectData = sickData[selectNum - 2];</code>
와 같은 조건의 경우, 최초에 ArrowDown이 발동하고, up을 할 경우는 예외 조건에 걸리기때문에 up에서의 else문이 발동되지 않습니다. 때문에 최소 2번이상의 ArrowDown 함수가 발동 이후에 up함수의 else부분이 발동되기때문에 -2를 통해 이전 값을 바라보게 됩니다.</p>
<p>이러한 방식으로 구현을 하게되었는데,</p>
<p>구현을 해가며, 한가지 예상치 못한 버그를 발견하였습니다.
인풋값이 영어일때는 로직이 예상대로 실행이 되었지만, 한글이 들어온경우에는 연속하여 2번 실행되는 버그였습니다.</p>
<p>찾아낸 문제에 대한 설명 및 해결법은 아래와 같습니다.
input에서 한글과 같은 조합식 문자를 사용하는경우, OS에서는 composition 이라는 과정을 거치게 됩니다. OS에서는 영어가 아닌 글자가 들어오면, 이것을 해당 언어 글자로 바꿔주는 이벤트를 처리하게됩니다. 이러한 과정에서 키보드 이벤트가 발생하면 OS와 브라우저 동시에 이벤트가 실행되기 때문에, 영어로 입력시에는 정상적으로 작동하지만, 한글의 경우에는 중복실행이 되는것이였습니다.</p>
<p>이 문제는 자바스크립트의 nativeEvent인 isComposing 옵션을 통해, 만약 컴포징중일시에는(OS와 중복 실행될 가능성이 있는 동안에는) 함수가 실행되지 않도록 처리하였습니다.</p>
<pre><code> const onKeyEventHanler = (e: React.KeyboardEvent&lt;HTMLTextAreaElement&gt;) =&gt; {
    if (e.nativeEvent.isComposing) {
      return;
    }
    const typing = e.code;
    upDownValHandle(typing, sickData);
  };
</code></pre><p>==&gt; 키보드 이벤트에서 isCopsing 중이면 함수가 실행되지 않도록 하였습니다.</p>
<h3 id="좋았던-부분-및-개선된-부분">좋았던 부분 및 개선된 부분</h3>
<ol>
<li>UI를 짜는 부분은 많이 발전한것 같습니다.</li>
<li>이전 service를 이용하는 모델은 처음 사용하는 것이기도하였고, class를 사용하는게 낯설엇는데, 이번 프로젝트에선 꽤나 개선된 모습을 보였고, 불필요한 로직을 줄였고, 로직을 잘 분리했던것 같습니다.</li>
<li>마찬가지로 커스텀훅은 익숙친 않은 방식이였는데, 이를 잘 활용하여 키보드 이벤트를 구현한것 같습니다.</li>
<li>TS를 공부환경이 아닌, 과제물에서 직접 사용해보는건 처음이였는데, 생각보다 수월하게 진행할수있어 좋았습니다.</li>
<li>디바운스 기능과 캐쉬 스토리지 사용등, 처음 사용하는것들을 알아보고 잘 사용할수 있었던것 같아 좋았습니다.</li>
<li>키보드 이벤트를 이용한 무한 리스트 이동형식은 주어진 요구사항은 아니였지만, 구현하게 되어 뿌듯했습니다.</li>
<li>마찬가지로 요구 사항 이외의 기능인, 저장된 캐시의 유효기간 이후 삭제 하는 방법 등을 생각하고 구현한 점 등이 좋았습니다.</li>
<li>인풋값 -&gt; 키보드 이벤트 발생시 예상치 못한 버그를 스스로 문제를 찾고 해결하게 되어 좋았습니다. (iscomposing 문제)</li>
<li>팀원 중 한분이 json 서버를 vercel에 배포하는 방법을 알려주셨고, 그곳의 API를 사용해 로컬환경이 아닌곳에서도 프로젝트를 배포하여 사용할수있게 되어 좋았고, json서버를 배포하는 방법을 알게되어 좋았습니다.</li>
</ol>
<h3 id="아쉬웠던-부분">아쉬웠던 부분</h3>
<ol>
<li>캐시 스토리지를 사용하는데, 이기능을 잘 분리시키지 못하였고, context 단에서 사용하려다보니, 결과적으로 URL을 하드코딩한 느낌이 있어 아쉬웠습니다. 이번 같은경우 service단에서 처리하는것이 더 좋앗을것이라 생각했습니다.</li>
<li>URL요청에 대해서 매 단어마다 체크를 하다보니, 매 단어를 가진 캐쉬스토리지가 오픈되었습니다. 이러한 것보다는 차라리 공통된 URL에서만 캐쉬스토리지를 만들고 사용하면 더 좋았을것이라 생각했습니다.</li>
<li>키보드 이벤트를 통한 리스트 결과 액티브 상태 이동을 원형 형식으로 무한하게 이동 가능하게 구현하였는데, 이 구현방법을 이해를 바탕으로한 구현보다는, 감에 의존한 구현을 하게 된것 같아 아쉬웠습니다.</li>
<li>커스텀 훅을 통하여 키보드 이벤트를 구현하였는데, 커스텀훅은 전역값이 아니다 보니, 하위 컴포넌트에서 동일한 props를 필요로 하다면 여러번 전달해주어야 해서, hooks를 사용하기보단 context를 사용하여 구현하는것이 더 옳은 방법이였던것 같습니다.</li>
<li>tailwind-styled-compoents Lib를 사용하고잇는데, 현재 TS와는 호환이 잘 안되는 이슈가 있습니다. 때문에 모든 컴포넌트에 any타입을 지정해주어야 하는데, 시간적 여유가 됏다면 twin.macro 등으로 마이그레이션 해보고 싶었는데 못한게 아쉬웠습니다.</li>
<li>장점이기도 하지만, 단점이기도 한 예상치 못한 버그를 찾는데 시간이 너무 오래 걸렸던것 같습니다. iscomposing는 정말 생각치 못한 이슈였기 때문에, 정확하게 문제를 찾는데 시간이 오래 걸렸던것 같습니다. 
버그가 발생하였을때, 우선적으로 어떠한곳에서 문제가 발생하는지 범위를 점점 좁혀나가고, 명확하게 이슈를 체크하는것이 중요한것 같다 생각했습니다.</li>
<li>완성해서 되돌아보니, 이번 프로젝트에서 로직을 압축할수 있었고, 이벤트 처리에 대한 이해도 조금 부족하였던것 같습니다.</li>
</ol>
<h2 id="배포-링크">배포 링크</h2>
<p>(작업 기간 약 2일)
<a href="https://pre-onboarding-7th-3-1-9-june.vercel.app/">https://pre-onboarding-7th-3-1-9-june.vercel.app/</a></p>
<h2 id="git-repo">Git Repo</h2>
<p><a href="https://github.com/jun-05/pre-onboarding-7th-3-1-9">https://github.com/jun-05/pre-onboarding-7th-3-1-9</a></p>
<h2 id="bestpractice-선정-결과">BestPractice 선정 결과</h2>
<p>캐싱 기능을 깔끔히 구현하셨고, 기타 요구사항이외의 것들을 모두 작성하신 분의 과제물이 BestPractice 로 선정되었습니다.</p>
<h2 id="진행-중-아쉬웠던-점">진행 중 아쉬웠던 점</h2>
<ul>
<li>원해서 한 팀장역은 아니였지만, 팀원 분들과의 정확한 의사소통이 잘 안되는것 같아 아쉬웠습니다.</li>
<li>코스가 중반을 넘어섰고, 여러모로 피로한 코스여서 그런지, 팀원분들이 지친것 같은 모습이 보여 아쉬웠습니다.</li>
</ul>
<h2 id="진행-중-좋았던-점">진행 중 좋았던 점</h2>
<ul>
<li>과제물의 요구사항에서 API요청을 줄이는것과, 캐싱 기능 구현에 대한 정보에 대한 의견이 빠르게 나왔고, 그에 대한 정보공유 및 사이트 공유가 잘 이루어져 좋았습니다.</li>
<li>캐싱 기능 구현 방법, 텍스트 bold 처리 방법 등, 그것에 대한 코드공유가 이루어져서 같은 문제여도 역시 여러가지 방법이 있다는것을 알게되어 좋았습니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WANTED] 프리온보딩 코스 week 3-1]]></title>
            <link>https://velog.io/@jun_53/WANTED-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%BD%94%EC%8A%A4-week-3-1</link>
            <guid>https://velog.io/@jun_53/WANTED-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%BD%94%EC%8A%A4-week-3-1</guid>
            <pubDate>Fri, 11 Nov 2022 19:43:06 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가기">들어가기</h1>
<p>이번강의에서는 이전 과제에 대한 피드백과, typescript에 대한 전반적인 지식을 다루었습니다.</p>
<p>(해당 게시글은 실제 강의 내용을 그대로 옮겨적은것은 아니며, 주관적인 이해 및 판단으로 요약되어진 글입니다.)</p>
<h1 id="강의-section_1">강의 Section_1</h1>
<h2 id="과제-피드백">과제 피드백</h2>
<p>대표적으로 제출된 각 팀들의 과제물에 대하여 피드백을 진행해주셨고, 대략적인 피드백은 아래와 같았습니다.</p>
<ol>
<li><p>전역 상태를 다루는 방법은 전역 상태 Lib 뿐일까?
시간을 비롯한 기타 옵션이 다루어진 Data들에 대한 값이 있는 page에서 다른 page로 이동하였고, 다시 돌아올시. 이러한 옵션들을 보관하기 위해선 전역을 꼭 사용할 필요가 없습니다.
기본적으로 사용하는 URL이 이를 대체할수 있기 때문입니다.
오히려 URL을 사용하는것이 더욱 좋을 수도 있다고 하셨습니다.</p>
</li>
<li><p>서버로부터 받아오는 데이터를 깔끔히 처리하는것이 좋다.
단순 계산 로직 / 요청을 날리는 비즈니스 관련 로직 / UI관련 로직
이러한 것은 가능한 분리 하는것이 좋다고 하셨습니다.</p>
</li>
<li><p>의존성 줄이기
이전에도 다루었던 문제긴 하였지만,
set과 같은 함수는 useEffect에 넣을 필요가 없고,
특정 함수가 useEffect의 의존성으로서 필요하다면
useEffect에서 함수를 생성하고 실행시키는것이 좋은 방법이라고 말씀하셨습니다.</p>
</li>
<li><p>보는 사람이 쉽게 볼 수 있도록 하는 코드란</p>
</li>
</ol>
<ul>
<li>변수명을 가능한 명확하게 하여야 합니다.</li>
<li>무성의한 함수 및 변수명은 지양해야 합니다. _1,_2 등..</li>
<li>조건문과 같은 로직이 필요한경우, 이것을 따로 함수로 만들어서 추상화 시키는것이 좋습니다.</li>
<li>의미없는 useCallback등은 사용하지 않는것이 좋습니다.</li>
<li>함수는 기능을 가능한 분리시키는것이 좋습니다.</li>
<li>조건이 통합 가능하다면, 통합을 통해 공통 로직을 줄이는것이 좋습니다.</li>
<li>컴포넌트를 꼭 같은 depth에 두는것보다는, 의미별로 나누어 보관하는것이 더 좋을 수 있습니다.</li>
</ul>
<ol start="5">
<li><p>함수를 의미에 맞게 사용하는것이 좋다
예를 들어 함수를 합치는 로직의 경우, forEach등을 통하여 로직을 짜는것보다는, reduce 함수를 사용하는것이 조금 더 의미에 맞다고 하셨습니다.</p>
</li>
<li><p>커링을 사용해서 함수를 좀 더 간결리 할 수 있다 하셨습니다.
예를 들어, 똑같은 param을 사용하는 함수들이라면, 커링을 사용한 공통 함수를 만들고, 2번째 값만을 변경하여 간단하게 사용할 수 있다 하셨습니다.</p>
</li>
</ol>
<pre><code>export const dataConverter = {
  getRoas: (start, ended) =&gt; {
    return returnConvert(start, ended, &#39;roas&#39;);
  },
  getCost: (start, ended) =&gt; {
    return returnConvert(start, ended, &#39;cost&#39;);
  },
  getImp: (start, ended) =&gt; {
    return returnConvert(start, ended, &#39;imp&#39;);
  },
};</code></pre><p>=&gt;함수를 담은 객체</p>
<pre><code>export const dataConverter = {
  getRoas: returnConvert(&#39;roas&#39;),
  getCost: returnConvert(&#39;cost&#39;),
  getImp: returnConvert(&#39;imp&#39;),
};</code></pre><p>=&gt; 커링 적용 함수 작성- 적용 후</p>
<h1 id="강의-section_2">강의 Section_2</h1>
<p>타입스크립트는 점점 프론트엔드 계열에서는 뉴 노멀이 되어가고있습니다. 21년 이후부터는 오히려 자바스크립트가 레거시 코드로 취급받을 정도입니다.</p>
<p>현 시점에서 타입스크립트는 필수에 가까운 skill set이 되어가고 있습니다.</p>
<h2 id="어째서-타입스크립트를-사용할까">어째서 타입스크립트를 사용할까?</h2>
<p>자바스크립트는 동적언어입니다.
또한 매우 자유도가 높은 언어입니다. 변수에는 정말 다양한 값이 들어갈 수 있습니다. 
이러한 특성을 가지기 때문에 런타임 시점, 실제로 동작하기 이전까지는 결과값을 대략적으로 예상은 하지만, 정밀하게 예측은 할수는 없습니다.
그렇기 때문에, 개발환경에서는 크게 무리가 없는 결과물이, 실제 프로덕션 환경에서는 예상치 못했던 값들이 들어올수있습니다.</p>
<p>이런 문제점을 갖고 있는 자바스크립트에서, 타입스크립트를 사용한다면 어떠한 변화가 있을지 살펴보겠습니다.</p>
<ol>
<li><p>이러한 예상치 못한 결과들을, 개발환경에서 변수 들을 정적타입으로 명시하여 비교적 쉽게 막을 수 있도록 도와줍니다.</p>
</li>
<li><p>또한, 사용해보면 생각외로 편리하다고 느껴질수 있습니다. 타입을 한번 잘 정리하면 이후 해당 값이 가지고 있는것을 판별하는게 매우 쉬워지기 때문입니다. (자동 완성기능 또한 제공)</p>
</li>
<li><p>문서화 기능을 가지고 있습니다.
다른사람의 코드 또는 Lib등을 보게되면, 이러한 코드가 어떠한 타입을 사용하는지를 알수 있게되어, 추상화의 기능으로서 코드를 알아보기 쉽게 합니다.
또한, 백엔드 개발자와 초기 셋팅을 통해 값을 정리해둔다면, 이후 백엔드 측과 많은 소통을 할 필요성이 줄어들것입니다.</p>
</li>
</ol>
<p>이러한 이유로 타입스크립트는 오늘날 많이 사용되어지고 있습니다.</p>
<p>++ gitHub의 Difinitely Typed라는 프로젝트에는 타입 정보만을 모아놓은 라이브러리가 있습니다. 
++ 타입스크립트는 런타임시에 다 사라지는, 일종의 개발환경 툴입니다.</p>
<h2 id="타입스크립트-기본">타입스크립트 기본</h2>
<h3 id="타입">타입</h3>
<p>이전 강의에서도 잠깐 다루었었는데, 자바스크립트의 타입은 원시형과 참조형이 있습니다. 참조형은 원시형이 아닌 값, 즉 Obj이고, 원시형은 Bool,String,number 등이 있습니다.</p>
<p>이러한 타입에서 원시형 타입은 의미에 맞게 사용하면 되지만,
Obj 타입은 범위가 매우 넓어집니다.
Obj 하위에는 Arr,객체,펑션,클래스 등 범위가 매우 넓어지기때문입니다.</p>
<p>그리고 모든 타입을 포함하는 any 타입이 있습니다.
이 any타입은 모든 타입을 포함하기때문에, 사용이 많아 질경우 타입스크립트를 사용하는 의미가 퇴색되어지기 때문에 사용이 지양되어 집니다.
unknown타입또한 모든 값을 할당 받을 수 있습니다. 하지만 unknown 타입의 경우 연산을 하는 경우 any와는 다르게 타입에 대하여 경고를 해줍니다.
any와 unknown 타입과 같이 모든 타입을 받을 수 있는 타입을 superset이라고도 합니다.</p>
<p>추가적으로 어떠한 값도 가지지 않는 Void.
Null과 undefined 타입. 
그리고 아무 값도 할당 할수 없는, 아무것도 가지지 않음 또는 불가능을 나타내는 Never 타입등이 있습니다.</p>
<h3 id="연산">연산</h3>
<p>타입스크립트에서는 합집합과 교집합 연산이 있습니다.</p>
<p>합집합의 경우 |를 사용하고, (union)
교집합의 경우 &amp;를 사용합니다.(intersection)</p>
<p>예시로서, string과 number 타입을 사용으로 두 가지 종류의 연산을 할 경우를 생각해보겠습니다. </p>
<p>string | number의 경우 합집합 적인 의미이긴하지만, 이 타입으로 지정할경우 <strong>두 타입 모두 사용할수 있는 함수</strong>만 호출할수있습니다.
하지만 &amp;으로 지정할경우, 아무런 함수도 호출할수 없습니다.
공통되는 부분이 전혀 없기 때문입니다.</p>
<p>하지만 이러한 &amp; 연산은 객체와 같은 값으로서 사용할때는 조금 달라집니다.
예를 들어, type C객체 = A객체 &amp; B객체가 있다면
C객체는 a와 b의 값을 모두 사용 할 수 있습니다.</p>
<p>반대로 |를 사용하였다면, 공통되는 부분의 값만을 사용할수있습니다.</p>
<h3 id="제네릭">제네릭</h3>
<p>제네릭은 타입을 인자로 받아, 더 정확하게 사용할 수 있게 해주는 도구입니다.
예를 들어 useState의 경우, 특정 지네릭을 사용하지 않았다면 타입을 자동으로 추론해주는데, 이것은 아래와 같은 형식으로 되어있습니다.</p>
<pre><code>useState&lt;S&gt;(initialState: S | (() =&gt; S)): [S, Dispatch&lt;SetStateAction&lt;S&gt;&gt;];</code></pre><p>지네릭 S 를 사용하여서, 어떠한 값이 들어올경우 들어오는 time에 타입을 자동으로 추론하여, 그 타입을 고정시켜준다는 의미입니다.</p>
<h3 id="타입가드">타입가드</h3>
<p>타입스크립트를 사용하는 경우, 유니온 | 을 사용하여 여러가지 타입이 존재 할 수 있습니다. 예를 들어 null | 특정 Type 이거나, string | string[] 등의 상황이 있을 수 있습니다.</p>
<p>이런 상황에서는 타입가드라는것이 필요합니다. 이것은 if를 비롯하여 여러방식으로 이루어집니다.</p>
<p>대표적인 방식인 if의 경우</p>
<p>특정 props의 타입이 null | string이라면
if(null) return처리를 통해
if 이후의 문장에서는 props를 string으로서 구분하게 됩니다.</p>
<p>null 또는 undefined와 같은 값이 들어가있는 경우, 타입가드가 거의 필수적으로 필요하게 됩니다.</p>
<p>다른 방법으서, 객체의 경우 in을 통하여 타입가드를 할 수 있습니다.</p>
<p>타입 A와 타입 B를 | 을 통해 받는경우,
props를 in을 통하여 타입가르를 할 수 있습니다.</p>
<p>추가적으로, 타입스크립트는 몇가지 예외적인 타입가드 처리가 있습니다.</p>
<p>filter와 reduce와 같은것인데, 
대표적인 예시는 아래와 같습니다.</p>
<pre><code>const arr = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, 1, 2, 3, 4]

const onlyNumber = arr.filter(character =&gt; typeof value === &quot;number&quot;)

console.log(onlyNumber)</code></pre><p>=&gt; 결과는 빈 배열</p>
<pre><code>const arr = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, 1, 2, 3, 4]

const onlyNumber = arr.filter((character): character is number =&gt; isNumber(character))

const isNumber = (value: any): value is number =&gt; {
  return typeof value === &quot;number&quot;
}</code></pre><p>=&gt; is Number를 사용하여 타입 체크 필요.</p>
<pre><code>const res = arr.reduce((acc, cur) =&gt; {
  return [...acc, cur.id + cur.name]
}, [])</code></pre><p>=&gt; acc[] never오류 발생</p>
<pre><code>const res = arr.reduce&lt;string[]&gt;((acc, cur) =&gt; {
  return [...acc, cur.id + cur.name]
}, [])</code></pre><p>=&gt; reduce에 필요로 하는 제네릭값 필요</p>
<h1 id="ts를-프로젝트에-적용해보기">TS를 프로젝트에 적용해보기</h1>
<p>1,타입 단언은 여러번 할필요가 없습니다.</p>
<p>타입스크립트는 타입만 제대로 명시되어 있다면, 이후 타입또한 알아서 추론하기 때문입니다.</p>
<p>2.any 타입은 제한적인 상황에서 사용 가능합니다.
예를 들어, 들어오는 값이 숫자 뿐만 아니라 여러가지 값이 들어오는 경우, 이를 체크하기 위한 함수를 만들기 위해선 any 타입을 사용 할 수 있을것입니다.</p>
<pre><code>const isNumber = (value: any): value is number =&gt; {
    return typeof value === &quot;number&quot;
}</code></pre><p>또는, try, catch 구문에서 catch의 err타입은 꼭 객체만 넘어오는것이 아니기때문에, any를 사용하는 경우가 많습니다.</p>
<ol start="3">
<li>기본 인터페이스를 존중하자
버튼에 대한 Props를 정의할때, 단순히 Props에 대한것만을 정의하는 것보다는, </li>
</ol>
<pre><code>export type Props = {
    theme: &#39;main&#39; | &#39;basic&#39;;
    type: &#39;submit&#39; | &#39;button&#39; | &#39;reset&#39;;
    disabled?: boolean;
    onClick?: (event: React.MouseEvent&lt;HTMLButtonElement&gt;) =&gt; void;
} &amp; React.ButtonHTMLAttributes&lt;HTMLButtonElement&gt;;</code></pre><p>와 같이 Props의 타입또한 같이 명시해주는것이 좋습니다.</p>
<p>++ TS에서 사용하는 타입과 인터페이스의 기능적인 차이는 없습니다. 하지만 의미적인 차이가 있을 수 있습니다.</p>
<h1 id="마무리-소감">마무리 소감</h1>
<p>사실 타입스크립트에 대해선 약간 두려워하는 감이 있엇는데, 강의를 통해 조금 더 잘 이해할 수 있게 됐던것 같습니다.
또한 피드백에서 말씀해주신 param을 전역 상태로 사용하는것이나, 의미에 맞는 함수 사용, 그리고 커링이라는 기법을 사용하여 함수를 간결히 사용하는 방법 등에 대한 내용도 새로운 시야를 볼 수 있게 해줬던것 같습니다.</p>
<p> 기억에 남는것 위주로 정리해보자면 아래와 같습니다.</p>
<ul>
<li>공통된 로직인 경우에는 커링 사용 고려</li>
<li>date와 같은 옵션값이 전역으로서 필요하다면 url을 사용해볼것을 고려</li>
<li>TS 사용시 타입가드가 필요하며, 일부 함수에서는 명시적인 타입을 요구하며, filter에서는 is와 같은것을 요구. 객체의 경우 in을 통해 타입가드</li>
<li>타입스크립트는 코드 작성시 수고를 더 들여야 하는 툴이 아닌, 수고를 줄여주는 툴</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WANTED] ASSIGNMENT_4]]></title>
            <link>https://velog.io/@jun_53/WANTED-ASSIGNMENT4</link>
            <guid>https://velog.io/@jun_53/WANTED-ASSIGNMENT4</guid>
            <pubDate>Tue, 08 Nov 2022 17:38:01 GMT</pubDate>
            <description><![CDATA[<h1 id="assignment_4">ASSIGNMENT_4</h1>
<h2 id="네번째-과제의-요구사항">네번째 과제의 요구사항</h2>
<p>과제의 대략적인 요구사항은 아래와 같았습니다.</p>
<ol>
<li>피그마로 페이지 디자인 시안이 주어져있고, 그것에 따라 구현합니다</li>
<li>페이지는 총 2개로, 각각 Header와 Nav를 공유합니다
2-2. 페이지 이동시 Nav에 있는 버튼은 이동한 페이지에 따라 디자인이 다르게 적용되어야 합니다.</li>
<li>1페이지에서는 광고 결과 데이터를 받아와 차트를 구성하고, 날짜 및 옵션에 따라 차트가 업데이트 되어야 합니다.</li>
<li>2페이지에서는 광고 정보 데이터를 받아와 카드 형식으로 데이터를 나타내고, 수정을 할 수 있어야합니다.</li>
</ol>
<h2 id="시작하기에-앞서">시작하기에 앞서</h2>
<h3 id="초기-셋팅">초기 셋팅</h3>
<p>초기 셋팅은 이전 과제물들과 동일하게 진행하였습니다.
불필요한 파일이 제거된 CRA를 생성하고,
ESLint,Prettier,husky가 설정이 된 repo를 main으로 올린 뒤, 팀원들이 모두 클론하여 각자의 브런치를 만들고 진행하였습니다.</p>
<p>추가적으로, 이번 과제에서는 API가 별도로 주어지지 않고 JSON 파일만이 주어졌기 때문에, public에 server 폴더를 만들고 거기에 보관하였습니다.</p>
<h3 id="gitmsg-규칙-변경">gitMsg 규칙 변경</h3>
<p>🎨 ui : 새로운 CSS관련 디자인에 대한 커밋
👏 chore : 파일 이동, 파일명 수정, 변수 제거 등의 자잘한 수정에 대한 커밋
📝 style : 공백 제거와 같은, 코드 스타일 혹은 포맷 등에 관한 커밋</p>
<p>과 같이 일부 커밋 규칙이 변경하였습니다.</p>
<p>기존 chore 규칙과 sytel 규칙의 경계가 너무 불분명하여, style 커밋은 자잘한 코드스타일에 대한 부분으로 제한하였습니다.
또한, ui 커밋을 따로 생성하여, 기존 feat커밋에 ui도 같이 겹치는 부분을 제거하기로 하였습니다.</p>
<h2 id="진행-및-bestpractice를-만드는-방법">진행 및 BestPractice를 만드는 방법</h2>
<p>이전 방식과 크게 달라진 부분은 없었습니다.
원칙적으로는, 과제 진행일 마다 매일 모여서 각자 의견을 공유하고, 제출 전날 각자 코딩리뷰를 한 후, BestPractice를 선출하였지만, 이번 과제물은 완성한 분이 한분밖에 없어서 자동적으로 그 분이 선출되었습니다.</p>
<h2 id="코딩구현">코딩구현</h2>
<h3 id="1-navlink를-사용한-button의-디자인-변경">1. NavLink를 사용한 Button의 디자인 변경</h3>
<pre><code> &lt;NavLink 
   to=&quot;/&quot; 
   className={({ isActive }) =&gt; 
     isActive 
       ? `${ServicePageButtonStyle} bg-[#EDEFF1] text-[#586CF5]` 
       : `${ServicePageButtonStyle}` 
   } 
 &gt; </code></pre><p>사실 react-router-dom의 Link만 사용하여서 NavLink를 사용할 생각은 전혀 못했었는데, 팀원분의 정보 공유로 인해 손쉽게 페이지 이동에 따른 버튼의 디자인변경을 할 수 있었습니다. 
NavLink는 클릭이 되어 페이지가 변경될시 className에서 isActive값을 주기 때문에, 이를 활용하여 버튼값의 디자인을 변경하였습니다.</p>
<h3 id="2-json-파일로-promise-요청하기">2. json 파일로 promise 요청하기</h3>
<p>이번 과제 같은경우 API가 전혀 주어지지 않고, JSON파일만이 주어졌습니다. 이것을 처리하기 위해서 어떠한 방식이 좋을지 토의해봤는데, json서버를 사용하거나, src 내에 data폴더에서 관리하여 불러오자는 의견등이 있었습니다. 하지만 팀원 한분의 의견으로 public에 json파일을 보관하고 해당 경로로 요청을 보내면 json 데이터를 API요청을 통해 받아오는것과 큰 차이없이 받아올수있다는 사실을 알게되었습니다.</p>
<p>정말 사소한것이지만, 이러한 발상으로 json파일을 코드에서 굳이 불러와 처리해줄 필요가 없다는것을 알게되어 좋았습니다.</p>
<pre><code>예시) axios.get(&quot;/mockData/jsonFile.json&quot;)</code></pre><h3 id="3-usetoggle-커스텀-훅의-재사용">3. useToggle 커스텀 훅의 재사용</h3>
<p>이것도 사실 매우 단순하고 사소한것이지만, 지금까지는 bool값이 필요한 많은 부분에서 각각의 컴포넌트에서 useState를 사용하여 값을 생성하였습니다. 하지만 이러한 부분을 커스텀 훅을 사용하여 재사용하였고, 이후에도 추상화 하여, 재사용할 수 있는 부분이 있다면 커스텀 훅을 사용 해야겠다는 생각을 하게되었습니다.</p>
<pre><code> export const useToggle = () =&gt; { 
   const [toggle, setToggle] = useState(false); 
   const onToggle = () =&gt; { 
     setToggle(prev =&gt; !prev); 
   }; 
   return [toggle, onToggle]; 
 }; 

 //사용 방법 (드랍다운)
  const [listToggle, onToggle] = useToggle();

    &lt;AdDropDownBlock onClick={onToggle}&gt; 클릭시 토글값 변경
       {listToggle &amp;&amp; &lt;토글일시 보여줄 값&gt;}
    &lt;AdDropDownBlock/&gt;</code></pre><h3 id="4-class를-사용한-service-및-context-사용">4. Class를 사용한 service 및 context 사용</h3>
<p>이전 강의에서 클린 코드를 위한 예시로서, OOP 관점에서 클래스가 하나의 관심사를 가지고 처리히먀, 또한 이것을 컨텍스트에서 값을 불러와 사용하는 예시를 강사님이 보여주셨습니다. 그래서 이번 프로젝트에서는 그러한 방식을 사용해보려 하였습니다.</p>
<pre><code>//getAdList(type:string):Promise&lt;adList[]&gt;
export class AdListService {
  #httpClient;
  #Listpath;
  #endPoint;

  constructor(httpClient, endPoint = &#39;wanted_FE_ad-list-data-set.json&#39;) {
    this.#httpClient = httpClient;
    this.#endPoint = endPoint;
    this.#Listpath = &#39;ads&#39;;
  }

  getAdList(type = &#39;all&#39;) {
    return this.#httpClient.fetch(this.#endPoint).then(result =&gt; {
      if (result.status !== 200) {
        throw new Error(&#39;api Error&#39;);
      }
      const adsData = result.data[this.#Listpath];
      if (type === &#39;all&#39;) {
        return Promise.resolve(adsData);
      } else {
        return Promise.resolve(adsData.filter(adItem =&gt; adItem.status === type));
      }
    });
  }
}
</code></pre><p>주석을 통해서 인터페이스를 적어주었고, httpClient와 endPoint를 의존성을 주입하여 사용할수있게 설계하였습니다. </p>
<pre><code>//context에서의 사용.. 처음에 값을 받아와 set으로 값을 넣습니다.
  useEffect(() =&gt; {
    adListService.getAdList().then(result =&gt; {
      setAdListData(result);
    });
  }, []);</code></pre><h3 id="5-익숙치-않은-lib를-사용하려한-경험">5. 익숙치 않은 Lib를 사용하려한 경험</h3>
<p>차트는 &#39;react-chartjs-2&#39; 를 사용해보려했고, 날짜를 표현하는 것은 airDatePicker를 사용해보려했습니다.
둘다 처음 사용해보는 Lib기 때문에 당연히 많은 부분을 해맸습니다.
결국 최종적으로 해당 Lib를 사용하여 과제물을 완성하지는 못하였지만,
모르는것에 도전한 경험이 좋았습니다.
완성은 못하였지만, chartJs는 배열을 사용하여 값을 주입하면 값이 자동으로 완성되고, DatePicker 또한 훅을 통해 값을 보관하면 손쉽게 사용이 가능하다는 점을 알게되었습니다.</p>
<p>그리고, 외부 Lib를 사용하는경우, 디자인은 무조건 2순위로 작업해야 한다는것을 절실히 깨닫게 되었습니다. 
이번 과제물 같은경우 Lib의 디자인에 신경쓰느라 시간 Resource를 많이 소비하였고, 결과적으로 만족하지 못한 결과가 나온것 같습니다.</p>
<h3 id="좋았던-부분-및-개선된-부분">좋았던 부분 및 개선된 부분</h3>
<ol>
<li>CSS 기능구현이 이전보다는 수월하게 진행되가는걸 느꼈습니다.</li>
<li>약간이지만, 반응형적인 디자인을 고려하여 페이지를 디자인한게 좋았습니다. grid와 flex의 혼합 사용등..</li>
<li>피그마에서 SVG를 따와서 조금 더 수월하게 진행할수있었습니다.</li>
<li>styled componet를 사용한 컴포넌트화를 많은 부분에서 진행하였습니다. </li>
<li>항상 사용하던 컨테이너 패턴을 하지 않고 class와 context를 사용한 클린코드 형식을 도전해본 경험이 좋았습니다.</li>
<li>NavLink를 사용하여 버튼의 디자인을 쉽게 적용이 가능하단점을 알게되어 좋았습니다.</li>
<li>비록 구현은 못하였지만, chart Lib를 어떻게 사용해야 하는지 간접적으로 알수있게 되어 좋았습니다.</li>
<li>HTML의 select 태그를 사용하지 않고, DropDown을 구현하는 방법을 구상하고 구현하게 되어 좋았습니다.</li>
<li>json파일을 public/server에 보관하여 해당 URL에 요청을 보내면 실제 API를 요청하는것처럼 사용할수있단점을 알게되어 좋았습니다.</li>
</ol>
<h3 id="아쉬웠던-부분">아쉬웠던 부분</h3>
<ol>
<li>시간내 많은 부분을 완성을 하지 못한게 가장 아쉬웠습니다.</li>
<li>chart를 사용하여 데이터를 표현하는것에서, 사실 디자인 부분은 2순위일것입니다. 가장 우선순위는 데이터를 보여주는것인데, chart의 디자인 부분을 다루는것에 시간을 너무 투자하였고, 결국 이것으로 인해 일정이 꼬여버린것 같습니다.</li>
<li>class를 사용한 클린코드를 구현해보려했는데, 처음 쓰는 방식이다보니 좀 헤맸던것 같습니다. 그저 fetch값을 전해주면되는데, 클래스 내부에서 불필요한 Promise를 사용하는등, 이상한 구현과, 구현을 하는데 해맨 시간이 꽤 많았습니다.</li>
<li>관심사 분리 원칙으로서, 하나의 클래스에서 너무 세세하게 나누려 한것 같아 아쉬웠습니다. 나쁜 방법은 아니겠지만, 완성이 최우선 사항일텐데, 사소할 수도있는 부분에 많은 시간을 투자하는것은 좋지 않다고 생각하였습니다.</li>
<li>url을 사용하여 페이지 이동 값 저장 또는 날짜 값과 같은것을 저장하는 방법을 생각하지 못한게 아쉬웠습니다.</li>
<li>수정하기 값을 전역값으로 저장하는 기능을 만들지 못한게 아쉬웠습니다.</li>
</ol>
<h2 id="배포-링크">배포 링크</h2>
<p>(기간 내 미완성,작업기간 1.5일)
<a href="http://pre-onboarding-7th-2-2-9-june.s3-website.ap-northeast-2.amazonaws.com/">http://pre-onboarding-7th-2-2-9-june.s3-website.ap-northeast-2.amazonaws.com/</a></p>
<h2 id="git-repo">Git Repo</h2>
<p><a href="https://github.com/jun-05/pre-onboarding-7th-2-2-9">https://github.com/jun-05/pre-onboarding-7th-2-2-9</a></p>
<h2 id="bestpractice-선정-결과">BestPractice 선정 결과</h2>
<p>최종적으로 시간내 완성하신분이 한분밖에 없으셔서, 그분이 자동적으로 선출되었습니다.</p>
<h2 id="진행-중-아쉬웠던-점">진행 중 아쉬웠던 점</h2>
<ul>
<li>과제 진행중에 스트레스와 피곤함을 많이 느꼈는데, 이것을 토의 진행중에 은연중에 한숨등으로 들어냈던점이 아쉬웠습니다. 스스로 힘들더라도 내색하지 않고, 파이팅한 느낌으로 진행하면 더 좋았을것 같습니다.</li>
</ul>
<h2 id="진행-중-좋았던-점">진행 중 좋았던 점</h2>
<ul>
<li>미완성이긴 하지만, 역시나 개발에 몰입하는 경험이 좋았습니다.</li>
<li>점점 팀의 규칙이 정형화 되어, 추가하거나 제거할 부분이 없어져, 신경 써야할 부분이 없어져서 좋았습니다.</li>
<li>정보 공유 Page를 모아두는 wiki 생성 등, 조금 더 정보를 문서화 하는 방향을 찾을 수 있었습니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WANTED] 프리온보딩 코스 week 2-2]]></title>
            <link>https://velog.io/@jun_53/WANTED-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%BD%94%EC%8A%A4-week-2-2</link>
            <guid>https://velog.io/@jun_53/WANTED-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%BD%94%EC%8A%A4-week-2-2</guid>
            <pubDate>Tue, 08 Nov 2022 13:27:17 GMT</pubDate>
            <description><![CDATA[<p>클린코드는 개발자라면 누구나 다 들어봤고, 선망하는 방식일것입니다. 이번 강의는 전체적으로 클린코드에 관하여 다루어졌습니다. 이번 강의를 통해, 어찌보면 의심않고 사용하여, 고정되어있던 제 코드 스타일에서 고쳐야 할점과, 좋은 방식의 코드란 무엇인가를 생각해보는 계기가 되었습니다.</p>
<p>(해당 게시글은 실제 강의 내용을 그대로 옮겨적은것은 아니며, 주관적인 이해 및 판단으로 요약되어진 글입니다.)</p>
<h1 id="강의-section_1">강의 Section_1</h1>
<p>이번 강좌에서는 이전 과제의 피드백 없이, 바로 강의 주제로 넘어가게되었습니다.</p>
<h2 id="클린코드란">클린코드란?</h2>
<p>클린코드는 사실 하나의 고정된 개발 단어는 아닙니다. 단지 Clean Code라는 책이 클린코드 작성법에 대해 설명하고, 또한 효율성이 입증되었기에 사람들이 많이 사용하는 단어일뿐입니다.</p>
<p>클린코드를 작성하려 한다는것은 좋은 코드를 지향한다는것이고, 이것은 곧 나쁜코드를 지양한다는것입니다.</p>
<p>나쁜 코드란 시간상의 이유등으로 이런식의 코드를 짜는 경우가 많습니다. 완성에 급급하여 하나의 로직이 이런저런 역을 맡다보면 결국에는 로직은 분리하고 수정해야 할 경우 작업이 너무 힘들어지게되고, 불가능하게 될 경우 소프트웨어 자체를 포기하게 된다는것입니다.</p>
<p><strong>클린코드란,</strong> 이런 나쁜 코드를 기피하고, 좋은 코드를 쓰려 하는것입니다.</p>
<p>그렇다면 나쁜 코드를 기피하기 위한 좋은 코드를 위해선 무엇을 해야 하는것일지 알아보겠습니다.</p>
<p>좋은 코드라고해서 정형화된 원칙은 없습니다. 하지만 많은 개발자들은 스스로의 노하우를 공개하였고, 이것들을 연구하여 좋은 코드를 작성하기 위한 원칙과 방법들을 쓰기로 한것입니다.</p>
<h2 id="관심사의-분리">관심사의 분리</h2>
<h3 id="관심사의-분리soc란">관심사의 분리(SoC)란</h3>
<p>분리 주제에 앞서, 관심사라는 단어는 간단히 말하면, 하나의 모듈이 수행하고자 하는 목적을 의미힙니다. (모듈은 함수,클래스 등의 수행하는데 필요한 단위라고 생각할수있습니다.)</p>
<p>관심사의 분리란 이러한 관심사를 잘개 쪼개어, 하나의 모듈이 폭 넓거나, 여러 관심사에 관여하게 작성되는것을 방지하는 것을 의미한다 할 수 있습니다.</p>
<p>이러한 관심사를 분리하는 이유는, 소프트웨어의 특정 부분의 수정이 필요로 하는 경우, 여러 모듈들이 여러 관심사를 동시에 다루고 있는 상황이라면 모든 모듈들을 일일히 수정해주어야 할것입니다. 하지만 관심사의 분리가 이루어진 모듈에서는 해당 모듈만 수정하면 됩니다.</p>
<p>하드웨어와 달리 소프트웨어는 수정,변화가 요구되는 경우가 잦습니다. 무형의 제품이기 때문입니다. 소프트웨어의 변화는 하드웨어와 달리 필연적이라 생각하여야 할 정도며, 좋은 소프트웨어 일수록 기능을 수정-확장하는것을 잘 할 수 있어야 합니다. 관심사의 분리와 같은것을 제대로 고려하지 못한 소프트웨어인 경우에는 수정이 매우 복잡해지고, 이것은 곧 변화가 되어야 하는 소프트웨어로서의 의미가 없어지게되고, 최종적으로 포기하게 되버리는 상황까지도 갈수있습니다.</p>
<p>이러한 관점에서 관심사의 분리는 유지-보수 측면에서 매우 유리한것입니다.</p>
<p>예시로서, 모든 페이지에서 필요로 하는 인증&amp;인가에 관한 기능을 수정해야 할때, 여러 모듈들이 이 모듈에 대하여 관여하고 있다면 수정이 매우 복잡해질것입니다.
하지만 인증&amp;인가에 대한 모듈을 하나만 작성하고, 이것을 다른 모듈이 사용만 하는경우에는 인증&amp;인가와 관련된 모듈 하나만을 수정하면 됩니다.</p>
<p>이와 같은 관심사의 분리는 소프트웨어를 만드는 프로그래밍에서 가장 기본이 되는 원칙이며, 이러한 개념을 표현하기 위한 단어와 격언들이 있습니다.</p>
<ul>
<li>단일 책임 원칙(Single Responsibility Principle) : 각 모듈은 책임을 가지고 있으며, 각 하나의 책임을 가져야 한다는 원칙.</li>
<li>KISS(Kepp It Simple, Stupid) : 각 모듈들은 간단하고, 단순하게 만들라는 의미이며, 하나의 기능만 수행하도록 하라는 의미</li>
</ul>
<h2 id="custom-hook">Custom Hook</h2>
<p>React는 UI를 구축하기 위한 라이브러리입니다.
즉, React의 핵심 관심사는 UI인것이며, 추가적으로 이 UI를 변경시키는 로직. 이 2가지로 나뉘게 됩니다.</p>
<p>이러한 관심사를 나누기 위해서, 개발자들은 몇가지 방식으로서 관심사를 분리하였습니다.</p>
<h3 id="presentational---container-방식">Presentational - Container 방식</h3>
<p>Presentational - Container 방식이란 컴포넌트를 2계층으로 분리하는 방법입니다.</p>
<p>React에서는 실제 보여지는 컴포넌트(Presentational)에서는 UI 변경에 필요한 Data 또는 이 Data의 가공 처리등이 필요합니다. 하지만 Presentational - Container 방식에서는 이러한 Data를 받거나, 가공 처리가 필요한 부분을 Container에서 모두 처리합니다. 
Container에서는 UI를 일체 담당하지 않으며, 컴포넌트에서는 마찬가지로 로직을 담당하지 않습니다. 이러한 방식을 사용하기 위해선 Container 컴포넌트에서 UI 담당 컴포넌트(Presentational) 를 감싸는 형태로 사용됩니다.</p>
<p>즉, Container에서는 데이터를 처리하고, 가공된 데이터를 Componet(Presentational)에 전달하여, 각각 UI와 로직의 관심사를 분리한것입니다.</p>
<p>하지만 이러한 방식은 Custom Hook의 등장으로 현재는 많이 사용되지 않는 패턴입니다.</p>
<h3 id="custom-hook-방식">Custom Hook 방식</h3>
<p>커스텀 훅이란 리액트가 기본적으로 제공하는 Hook들을 사용하여 만든 함수를 말합니다.
커스텀 훅의 기본 원칙은 2가지 입니다.</p>
<ol>
<li>리액트의 Hook을 호출,사용하는 함수여야 합니다.</li>
<li>함수의 이름은 무조건 use로 시작하여야 합니다.</li>
</ol>
<p>커스텀 훅을 사용하는 이유는 아래와 같습니다.</p>
<ol>
<li>동일한 로직이 반복되어서 사용되는 경우, 일반적으로 함수를 따로 빼서 만들듯이, 리액트에서도 동일한 기능이 반복되는 경우, 이것을 커스텀 훅을 만들어 사용하게 됩니다.</li>
</ol>
<ul>
<li>예시로서, Toggle기능과 같이 많은 부분에서 재사용되는 bool값을 필요로하는 state를 필요로 하는 경우가 있는데, 이것을 매번 각 컴포넌트에서 호출하는것보다는, 커스텀 훅을 생성하여 사용한다면 조금 더 간결하게 표현할수있습니다.</li>
</ul>
<ol start="2">
<li>관심사를 분리하는 방법으로서, 해당 관심사만을 처리하는 모듈로서의 커스텀 훅을 만들 수 있습니다.</li>
</ol>
<ul>
<li>예시로서, 유저의 인증&amp;인가를 처리하는 커스텀 훅을 만들어 컴포넌트에서 사용하면 컴포넌트에서는 그저 받은 값을 사용하고, 로직은 훅에서 관리하기 때문에 UI와 로직의 관심사가 분리되는것입니다.</li>
</ul>
<h1 id="강의-section_2">강의 Section_2</h1>
<h2 id="횡단-관심사">횡단 관심사</h2>
<p>횡단 관심사는 여러 서비스에 걸쳐서 동작해야 하는 관심사(코드,모듈)를 의미합니다.</p>
<p>서비스 A,B,C에서 모두 필요로 하는 로직이 있다하면,이것이 마치 서비스들을 횡단하는것과 같기때문에 횡단 관심사라 불립니다.</p>
<p>인증&amp;인가 / 로깅 / 에러처리 / 트랜잭션 처리 등이 대표적인 횡단 관심사입니다.</p>
<p>횡단 관심사는 관심사의 분리 측면에서 중요한데, 이 관심사가 혼재되어 버리면 수정하기가 매우 힘들어지기 때문입니다.</p>
<h2 id="http-통신에서-횡단-관심사-처리하기">HTTP 통신에서 횡단 관심사 처리하기</h2>
<p>가장 흔하게 생각할 수 있는 관심사는 인증&amp;인가입니다.
Http 요청을 보낼때, 프론트엔드단에서는 매번 헤더에 특정 값을 보내야 하는 경우가 많습니다. 특히 Token과 같은 값이 그렇습니다.</p>
<p>이런 경우 아래와 같은 형태로 통신을 보내게 될것입니다.</p>
<pre><code>    fetch(&quot;url...&quot;, {
      headers: {
        Authorization: &quot;ACCESS_TOKEN&quot;
      }</code></pre><p>하지만 이러한 형식으로 매번 요청을 보내는것은 비 효율적입니다. 이러한 상황에서 url과 token값을 넣는 기능은 횡단 관심사로서 작용한다면, 아래와 같이 그 기능만을 담당하는 모듈을 만들 수 있습니다.</p>
<pre><code>class HttpClient {
  constructor(baseURL) {
    this.baseURL = baseURL;
  }

  fetch(url, options = {}) {
    return fetch(`${this.baseURL}${url}`, {
      ...options,
      headers: {
        Authorization: sessionStorage.getItem(&quot;access_token&quot;),
        ...options.headers
      }
    });
  }
}

//다른 곳에서 사용
const httpClient = new HttpClient(url...);
httpClient.fetch(endPoint..);
</code></pre><p> 위와 같은 방법으로 작성하다면, 기존 횡단 관심사인 fetch의 url을 유연하게 수정할수있고, 컴포넌트 내에서 호출할때 훨씬 간결하게 호출하는 방식의 클린코드를 만들수있습니다.</p>
<p>추가적으로 클래스를 사용하는 방식의 경우 baseURL 생성자와 같은 부분을 #baseURL를 사용하여, private 값으로 취급할수있습니다. 이렇게 사용할경우 해당 클래스를 호출하였을때 생성자 정보를 은닉할수있습니다.</p>
<h1 id="강의-section_3">강의 Section_3</h1>
<h2 id="의존성-역전-원칙">의존성 역전 원칙</h2>
<p>시작에 앞서, 추상과 구체에 대해 설명하고자 합니다.
추상이란, 기대하는 결과만을 적어놓은것과 같습니다.
예를 들어, getTodo라는 형태만을 만들어놓고, 실제 구현은 해놓지 않으면 이것은 추상입니다. 단지, 이름에서 알수 있듯이 함수를 실행시키면 Todo를 줄것이라 예상할수있습니다.</p>
<p>getTodo를 실제로 구현한 함수는 구체입니다. 실제 로직을 작성하여 흐름을 제어해야만 합니다. </p>
<p>구체는 여러 이유 등으로 수정되어야 하는 경우가 많습니다. 
하지만 추상은 수정되지 않습니다. 추상을 구현하는 구체가 수정이 필요하다면 수정할 뿐입니다.</p>
<p>다시, 의존성이란 주제로 돌아와서,
<strong>의존성</strong>이란 특정한 모듈이 동작하기 위해서 다른 모듈을 필요로 한다는것을 말합니다.</p>
<p>즉, 구체를 실행시키고자 할때, 다른 구체가 필요로 하는 경우를 말합니다.</p>
<p><strong>의존성 역전 원칙이란</strong> 코드의 의존성이 추상에 의존하며, 구체에는 위존하지 않는것을 의미합니다.
이는 곧, 유연성이 극대화된 시스템을 만들기 위한 원칙입니다.</p>
<p>요구 사항이 변한다면 추상에 의존한 애플리케이션을 작성한 경우에는 수월하게 수정 할 수있지만, 구체에 의존한 애플리케이션은 구체가 의존성을 가질경우 수정하기가 힘들어질수 있습니다.</p>
<p>코드를 통해 예를 들어보겠습니다.</p>
<p>token을 이용하여 api 통신을 하려는 함수가 있을경우, 일반적으로 아래와 같이 작성할것입니다.</p>
<pre><code>fetch(&quot;todos&quot;, {
    headers:{
        Authorization:localStorage.getItem(&quot;ACCESS_TOKEN&quot;);
    }
}</code></pre><p>하지만 위 코드는 localStorage라는 구체에 의존하고있습니다.</p>
<p>단순히 저러한 fetch를 사용하는 함수 뿐만 아니라, 비슷하게 localStorage의 토큰을 사용하는 함수는 모두 localStorage.getItem(&quot;ACCESS_TOKEN&quot;); 라는 의존성을 가지고 있는것입니다.</p>
<p>이러한 토큰의 의존성을 제거하는 방법으로, token만을 담당하는 모듈을 만드는것이 좋을 수 있습니다.</p>
<p>아래와 같은 모듈을 만든다면 token값의 의존성을 제어할 수 있습니다.</p>
<pre><code>/*
    TokenRepositoryInterface

      save(token:string):void
      get():string
      remove():void
*/

class LocalTokenRepository {
  #TOKEN_KEY = &quot;ACCESS_TOKEN&quot;;

  save(token) {
    localStorage.setItem(this.#TOKEN_KEY, token);
  }

  get() {
    return localStorage.getItem(this.#TOKEN_KEY);
  }

  remove() {
    localStorage.removeItem(this.#TOKEN_KEY);
  }
}

const tokenRepository = new LocalTokenRepository();</code></pre><p>우선적으로, 주석으로 처리된 부분은 추상(인터페이스)입니다.
해당 부분을 통해 행동과 결과만을 기술합니다.</p>
<p>그리고 LocalTokenRepository라는 클래스는 인터페이스를 따라야하만 하는 구체 모듈입니다.
구체는 추상에서 정의해둔 형태로구현해야 합니다.</p>
<p>위와 같은 코드를 구현한다면, 우선적으로 localStorage가 변경되어야 하는 경우 해당 모듈만 수정하면 됩니다. </p>
<p>또한,기존 코드의 흐름이 API 호출 -&gt; localStorage 흐름에서</p>
<p>API 호출 코드 → tokenRepository Interface → tokenRepositry Class → localStorage 흐름으로 바뀌게 됩니다.</p>
<p>하지만 의존성적인 측면에서는 tokenRepository Interface ← tokenRepositry Class → localStorage 라는 흐름으로 바뀌게 됩니다.</p>
<p> 클래스의 구체에 의존해 실현은 되지만, 클래스 자체는 인터페이스에 의존하게 됩니다.
이와 같이, 코드의 실행 흐름과 의존성이 방향이 반대로 뒤집힌 상황을 의존성 역전 원칙이라 부르며,IoC(Inversion of Control)이라고 표현합니다.</p>
<h2 id="의존성-주입">의존성 주입</h2>
<p><strong>의존성 주입이란</strong>  특정한 모듈에서 필요한 의존성을 내부에서 가지고 있는게 아니라, 해당 모듈을 사용하는 입장에서 입력(주입)하여 사용하는 형태로 설계하는것을 의미합니다.</p>
<p>마찬가지로, 클래스를 예시로 들자면
constructor를 사용하지 않고, 자체적으로 내부에서 정의된 값을 사용하는 클래스와
constructor를 사용하여, 외부로부터 선언시 해당 값을 사용하는 클래스가 있다면</p>
<p>전자의 클래스는 유연하지 않습니다. 때문에 의존성이 모듈내부에 제한되어있습니다
하지만 후자와 같은 클래스는 값,의존성을 주입받아 유연하게 대처할수 있습니다.</p>
<p>의존성 주입이 없는 클래스 (url과 )</p>
<pre><code>class AuthService {
  signup(email, password) {
    httpClient
      .fetch(&quot;auth/signup&quot;, {
        method: &quot;POST&quot;,
        body: JSON.stringify({
          email,
          password,
        }),
      })
      .then((res) =&gt; res.json())
      .then(({ access_token }) =&gt; tokenRepository.save(access_token));
  }</code></pre><p>의존성 주입을 이용하는 클래스 ()</p>
<pre><code>class AuthService {
  constructor(httpClient, tokenRepository) {
    this.httpClient = httpClient;
    this.tokenRepository = tokenRepository;
  }

  signup(email, password) {
    this.httpClient
      .fetch(&quot;auth/signup&quot;, {
        method: &quot;POST&quot;,
        body: JSON.stringify({
          email,
          password,
        }),
      })
      .then((res) =&gt; res.json())
      .then(({ access_token }) =&gt; this.tokenRepository.save(access_token));
  }</code></pre><p>두 코드 모두 httpClient와 tokenRepository라는 클래스를 사용하고있습니다.
하지만 앞의 코드는 httpClient와 tokenRepository에 의존하고 있기 때문에, 만약 관련 동작을 수정하려 한다면 AuthService 자체를 수정해야 합니다.</p>
<p>하지만 아래의 코드와 같이, 클래스를 생성할때 외부에서 주입하는 형식으로 변경하게 되면 이후에 AuthService의 코드수정없이 httpClient와 tokenRepository에서 연관된 동작을 쉽게 변경해서 다양하게 사용할수있습니다.</p>
<h1 id="마무리">마무리</h1>
<p>이번 강의에서는 클린코드에 대해서 알아보았습니다.
웹 개발을 배우면서 클린 코드를 잘은 모르지만 해보고 싶다는 생각은 항상 가지고 있었습니다.
하지만 어떻게 해야하는지, 무엇을 중요시해야 하는지는 막연했습니다.
이번 강의를 통해서 대략적으로 클린코드를 위해서는 어떠한것을 중요시 해야하는지 알수 있게 되어 이후 개발에서는 이런 관점에서 코드를 짜야겠다는 생각을 할수있게 되었습니다.</p>
<p>이번강의를 통해서 해보고자 한것은 아래와 같습니다.</p>
<ul>
<li>관심사 분리를 위해, 함수는 가급적 단일책임 원칙을 지켜서 만들것</li>
<li>공통되는 함수는 커스텀 훅을 짜서 만들어보자</li>
<li>횡단 관심사 모듈이 무엇일지 생각해보고, 관심사를 분리한 모듈을 만들기</li>
<li>기존 Presentational - Container만을 사용하던 방식에서, 커스텀 훅을 사용한 클린코드를 써보려고 노력하기</li>
<li>클래스를 사용한 Service를 만들어서 의존성 역전 원칙 등을 적용한 유연한 코드 작성해보기.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WANTED] ASSIGNMENT_3]]></title>
            <link>https://velog.io/@jun_53/WANTED-ASSIGNMENT3</link>
            <guid>https://velog.io/@jun_53/WANTED-ASSIGNMENT3</guid>
            <pubDate>Sat, 05 Nov 2022 09:07:34 GMT</pubDate>
            <description><![CDATA[<h1 id="assignment_3">ASSIGNMENT_3</h1>
<h2 id="세번째-과제의-요구사항">세번째 과제의 요구사항</h2>
<p>세번째 과제의 요구사항은 대략적으로 이러하였습니다.</p>
<ol>
<li>피그마로 디자인이 주어졌고, 피그마 상의 디자인 및 기능을 구현하여야 했습니다.</li>
<li>디자인은 모바일 기준으로, 360~450px까지 고려해서 제작되어야 했습니다.</li>
<li>API가 주어졌고, API의 상태에 따라 로딩 중, 차량없음 등이 나타나야했습니다.</li>
<li>페이지는 두개로 나뉘어지며, 리스트 페이지와 상세페이지 두개로 나뉘어졌습니다.</li>
<li>추가 요구사항으로 SEO를 구현하여, 카카오톡 및 페이스북에 공유시 일부 정보가 노출되어야 합니다.</li>
</ol>
<ul>
<li>과제 수행 시간 수요일 오전 0시~ 금요일 오전 9시 (약 2일)</li>
</ul>
<h2 id="시작하기에-앞서">시작하기에 앞서</h2>
<h3 id="초기-셋팅">초기 셋팅</h3>
<p>초기 셋팅은 이전 과제물들과 동일하게 진행하였습니다.
불필요한 파일이 제거된 CRA를 생성하고,
ESLint,Prettier,husky가 설정이 된 repo를 main으로 올린 뒤, 팀원들이 모두 클론하여 각자의 브런치를 만들고 진행하였습니다.</p>
<h3 id="코딩-컨벤션">코딩 컨벤션</h3>
<p>코딩 컨벤션은 이전과 유사하지만, 추가적으로 loading과 error에 대한 상태값을 isLoading, hasError으로 표현하기로 하였습니다.</p>
<h5 id="기존-코딩-컨벤션">기존 코딩 컨벤션</h5>
<ul>
<li>컴포넌트의 ID사용은 지양한다.</li>
<li>react의 state는 여러개 사용시 최소 집합을 찾아 사용한다.</li>
<li>컴포넌트의 이벤트에서 불필요한 익명함수를 사용하지 않는다. (예시: 함수의 인자가 event 하나인 경우)</li>
<li>코드를 설명하는 주석은 가급적 사용하지 않는다.</li>
<li>상수는 영문 대문자 스네이크 표기법(Snake case)를 사용한다.(예시: SYMBOLIC_CONSTANTS)</li>
<li>반환 값이 불린인 함수는 &#39;is&#39;로 시작한다</li>
<li>const와 let은 사용 시점에 선언 및 할당한다.</li>
<li>함수는 사용 전에 선언해야 하며, 함수 선언문은 변수 선언문 다음에 오도록 한다.</li>
<li>이벤트 핸들러는 &#39;on&#39;으로 시작한다.</li>
<li>한 줄짜리 블록일 경우라도 {}를 생략하지 않으며 명확히 줄 바꿈 하여 사용한다.<ul>
<li>한 줄짜리 블록일 경우 {}를 생략할 수 있지만, 이는 코드 구조를 애매하게 만든다. 당장은 두 줄을 줄일 수 있겠지만 이후 오류 발생 확률이 높아 잠재된 위험 요소가 된다.</li>
<li>단, map과 같은 화살표 함수의 암시적 반환은 허용한다.</li>
</ul>
</li>
</ul>
<h2 id="진행-및-bestpractice를-만드는-방법">진행 및 BestPractice를 만드는 방법</h2>
<p>이전 방식과 크게 달라진 부분은 없었습니다.</p>
<p>기본이 개인과제로서 이뤄져야 하는 프로젝이트이고, 시간적인 여유가 없는 편이였기에 이상적인 BestPractice를 만들수는 없었습니다. </p>
<p>하지만 하루에 한번씩 토의시간을 가져, 각자 진행상황 및 정보를 공유하였고, 좋다고 생각하는 방법을 Discusions에 공유하여 장점을 자신의 프로젝트에 적용할 수 있도록 하였습니다.</p>
<h4 id="선정방법">선정방법</h4>
<p>기본적으로 각자 repo를 진행하였고, 제출 하루전날에 Discusions에 배포 페이지와 함께 코드를 공유하였고, 제출날 오전 9시까지 각자 코드리뷰를 끝낸 후, BestPractice를 선출하였습니다.</p>
<h2 id="코딩구현">코딩구현</h2>
<p>개인적인 구현 과정에서 막혔던 부분의 List는 아래와 같았습니다.</p>
<h3 id="1-css-관련-부분">1. CSS 관련 부분</h3>
<p>해당 과제의 Header는 전통적인 Header 방식이였습니다.
EL1 Title EL2
타이틀은 중앙정렬, 나머지 EL은 각 왼쪽,오른쪽에 정렬된채로 나타면 되는 부분입니다. 쉬울수 있다 생각했던 부분인데, EL이 width를 먹어버리니, flex로 구현햇을때 justify-center와 같은 부분이 약간 어긋난채로 진행되었습니다. 결과적으로는 EL2는 원래는 없는 컴포넌트지만, spacer로서 EL1과 같은 크기를 주었고, flex의 설정중 flex-none 과 grow 설정을 통하여 해결하였습니다.</p>
<p>하지만 결과적으로 absoulte를 활용했다면 더욱 쉽게 해결할수 있지 않았나 생각하였고, 너무 flex에 의존하여 페이지를 구성하려 하지 않았나 반성하였습니다.</p>
<h3 id="2-tag-정보를-받아오는-부분">2. tag 정보를 받아오는 부분</h3>
<p>해당 과제물에서는 Label과 같은, 태그를 선택하여 정보를 받아옵니다. 이 태그는 사실 API값으로 받는것이 아닌, 어느정도는 정해진 값이기때문에 상수등을 사용한 하드코딩이 어느정도 허용되는 부분이였습니다.</p>
<p>저는 이부분을 오해하여 API를 사용하여, 태그에 사용되어지는 2가지 타입의 정보를 2차원 배열로 타입 이름과 API 상의 VALUE를 넣고, 2차원 배열의 중복값을 제거하고, 이후 ENUM값에 따른 INFO VALUE값을 넣어주었습니다.</p>
<p>오해한 부분 또한 아쉽고 개발 리소스가 낭비된 부분이지만, 2차원 배열의 중복값을 제거하는 부분에서 시간이 오래 걸린것 같습니다.
2차원 배열의 중복값 제거는 결국 검색을 통하여 해결하였습니다.<code>removeDup 함수</code></p>
<pre><code>ENUM 타입기술... 

function getTagArr(carObj) {
  const allTag = carObj.reduce((acc, cur) =&gt; {
    let carInfo = cur.attribute;
    return [...acc, [&#39;fuelType&#39;, carInfo.fuelType], [&#39;segment&#39;, carInfo.segment]];
  }, []);

  const uniqueTag = removeDup(allTag);

  return uniqueTag;
}

function tagFiltering(carObj) {
  const allTag = getTagArr(carObj);
  const tagItem = allTag.map(tagArr =&gt; {
    if (tagArr[0] === &#39;fuelType&#39;) {
      return [...tagArr, fuelType[tagArr[1]]];
    } else if (tagArr[0] === &#39;segment&#39;) {
      return [...tagArr, setmentType[tagArr[1]]];
    }
    return null;
  });

  return tagItem;
}
</code></pre><h3 id="3-전역값을-활용하는-부분">3. 전역값을 활용하는 부분</h3>
<ol>
<li>과제물의 요구사항은 상세페이지에서 뒤로가기 페이지 이동시 이전 정보값이 남아있어야 합니다. 마찬가지로 Tag 또한 선택된 값이 남아있어야 합니다. 페이지가 이동시 컴포넌트 자체는 리렌더링 됩니다. 때문에 이전값을 재 활용할수없었습니다. 저는 이부분을 React.memo를 활용하여 Tag가 리렌더링 되지 않도록 하고 싶었지만, 의존성적인 문제로 인해 쉽지 않았습니다. 결국 헤매다 Tag의 정보 또한 전역값을 선택하여 뒤로가기시 불러오도록 하였습니다.</li>
</ol>
<p>전역값은 최소로 하는게 맞지만, 필요하다 생각하면 이용을 주저하지 않는것 또한 중요하다 생각하였습니다.</p>
<h3 id="좋았던-부분-및-개선된-부분">좋았던 부분 및 개선된 부분</h3>
<ul>
<li>컴포넌트화를 이전보다는 더 할 수 있도록 노력하였습니다.</li>
<li>tailwind-styled-component를 활용하여 가시성을 조금 더 좋게 할 수있도록 노력하였습니다.</li>
<li>리액트의 state 사용을 가능한 최소화 하기 위해 노력하였습니다.</li>
<li>단순한 화면 구현 이외에도, 생각나는 오류를 최소한으로 할 수 있도록 노력하였습니다.</li>
</ul>
<h3 id="아쉬웠던-부분">아쉬웠던 부분</h3>
<ol>
<li>CSS부분에서 막히는 부분이 부분부분 있어 생각보다 시간이 많이 걸렸습니다.(absolute의 설정, flex의 공간 설정 등)</li>
<li>설정적인 부분을 미처 제대로 확인하지 못해 작동이 안되는걸 발견하는데 시간이 소요되었습니다.</li>
<li>시간분배를 잘못하여, 제출일정까지 완성이 빡빡한채로 진행되었습니다. 그로인해 함수의 추상화 및 변수명, 변수의 사용, 디테일 페이지의 일부 컴포넌트 분리 등을 정리되지 않은채로 제출한게 아쉬웠습니다.</li>
<li>useEffect를 매 렌더링시 실행시키고, 조건문을 주어 실행 시키지 않는 방법등의, 해당 프로젝트에서 더 좋은 방법이 있는것을 진행중에는 미처 깨닫지 못한 부분이 아쉬웠습니다.</li>
<li>context 또는 redux와 같은 전역값을 언제 어디에 보관해주어야 할까를 빠르게 정하지 못해 헤맸던 부분이 있었습니다.</li>
<li>API와 피그마 상의 태그정보를 불러오는것을 잘못 해석하여, 개발 리소스의 낭비를 하였던 부분이 아쉬웠습니다.</li>
<li>component - container 구조의 파일작성을 하엿는데, 일부 컴포넌트(태그)에서 컨테이너에서의 데이터 주입이아닌, 직접적인 전역값을 불러와 사용하는것이 아쉬웠습니다.</li>
<li>추가요구사항 REACT-SEO는 구현하지 못하였습니다.</li>
</ol>
<h2 id="배포-링크">배포 링크</h2>
<p>(작업 기한 약 2일)
<a href="http://wanted-pre-onboarding-fe-7-june.s3-website.ap-northeast-2.amazonaws.com/">http://wanted-pre-onboarding-fe-7-june.s3-website.ap-northeast-2.amazonaws.com/</a></p>
<h2 id="git-repo">Git Repo</h2>
<p><a href="https://github.com/jun-05/pre-onboarding-7th-2-1-9">https://github.com/jun-05/pre-onboarding-7th-2-1-9</a></p>
<h2 id="bestpractice-선정-결과">BestPractice 선정 결과</h2>
<p>이전과 동일하게 Discusions을 통하여 의견을 나누고, 최종적으로 선정된 분의 과제물을 최종 과제물로 제출하였습니다. 이분의 코드에서 좋다고 생각하였던 부분은 아래와 같습니다.</p>
<p>NextJS를 처음 접해서 공부하고, 바로 프로젝트에 적용하신 분이셨습니다.
때문에 아래와 같은 점으로 저는 이분을 선정하였습니다.</p>
<ul>
<li>NextJS를 빠르게 습득하고 적용하시는 점이 정말 본 받고 싶었습니다.</li>
<li>프로덕트 완성을 여유롭게 하신점 또한 정말 본 받아야지 싶었습니다.</li>
<li>기본적인 요구사항 충족 뿐만 아니라, 세심한 부분 까지 신경쓰신점이 좋았습니다.</li>
<li>애니메이션 Lib를 잘못사용하게되면 어색할 수도있는데, 어울리게 구현하셨습니다.</li>
<li>API에서 id는 있었지만, id를 받아 처리하는 부분이 없었는데, 이부분을 filter값을 사용하여 id값으로 페이지를 구현하였습니다.</li>
<li>세세한 부분, 전역 값이 있을때 뒤로가기를 누르면 값을 재활용하고, 없으면 전체를 모두 불러오는 등의, 세세한 처리가 좋았습니다.</li>
</ul>
<p>선정 결과 링크(비공개)</p>
<h2 id="진행-중-아쉬웠던-점">진행 중 아쉬웠던 점</h2>
<p>코드부분을 제외한 많은 부분이 개선되었다고 생각했습니다.
하지만 그래도 아쉬운 부분은 있었고, 그러한것은 아래와 같습니다.</p>
<ul>
<li>초기셋팅에 알맞는 favicon을 공통된 CRA에 넣을것을 건의 하면 더 좋았을것 같습니다.</li>
<li>공유 코드 외, 정보 공유 사이트를 깃허브의 wiki를 사용하여 보관한다면 더 좋았을것이라 생각했습니다.</li>
<li>gitMsg 규칙에서 일부 혼동되어 사용될 여지가 있는채로 프로젝트가 진행되었습니다.</li>
<li>문서 작업 및 템플릿 작업도 맡다보니, 시간 분배가 안되어 정작 본인의 프로젝트에 소홀히 했던 부분이 있던것 같습니다.</li>
</ul>
<h2 id="진행-중-좋았던-점">진행 중 좋았던 점</h2>
<ul>
<li>이전과 마찬가지로, 짧은 시간내 몰입하여 프로덕션 개발을 완성하는 경험을 하였습니다.</li>
<li>팀원 분들의 정보공유가 조금 더 활발히 진행될 수 있었던것 같습니다.</li>
<li>토의시 진행을 조금 더 매끄럽게 진행할 수 있었던것 같습니다.</li>
<li>Discussion을 적극적으로 활용하여 매 토의 내용을 가능한 모두 적을 수 있도록 노력하였습니다.</li>
<li>Discussion에 올리는 양식에서 필요한 부분은 공통적으로 기술하여 조금씩 규격화 하게되어 코드리뷰시 이전보다는 매끄러워진 부분이 좋았습니다.</li>
<li>readMe 템플릿을 작성하고 공유하여, 선정된 분이 readMe 작성시 도움이 될 수 있도록 한 점이 좋았습니다.</li>
<li>마크다운에 조금 씩 익숙해 져가며, permaLink, 토글 및 기타 사용법에 대해 익숙해진 부분이 좋았습니다.</li>
<li>AWS S3를 사용하는 배포에 대해서는 매우 익숙해진것 같습니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WANTED] 프리온보딩 코스 week 2-1]]></title>
            <link>https://velog.io/@jun_53/WANTED-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%BD%94%EC%8A%A4-week-2-1</link>
            <guid>https://velog.io/@jun_53/WANTED-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%BD%94%EC%8A%A4-week-2-1</guid>
            <pubDate>Fri, 04 Nov 2022 19:46:53 GMT</pubDate>
            <description><![CDATA[<h1 id="강의-section_1">강의 Section_1</h1>
<p>세번째 강의의 첫 섹션은 이전과 마찬가지로
첫 번째 과제에 대한 피드백이 이어졌습니다.</p>
<p>(해당 게시글은 실제 강의 내용을 그대로 옮겨적은것은 아니며, 주관적인 이해 및 판단으로 요약되어진 글입니다.)</p>
<h2 id="첫번째-주제--과제-피드백">첫번째 주제 : 과제 피드백</h2>
<h3 id="good-point">Good Point</h3>
<ul>
<li><p>상수화,이름으로 의미 부여하기
컴포넌트 에서 특정 조건을 렌더링 하려할때, 단순히 Idx값이나. 주어진 props 등을 이용하기보단, 그 값이 고정된 값이라면 상수를 사용하는것이 좋습니다.
(idx ===4 라는 조건보다는, idx === AD_INDEX 등..)</p>
</li>
<li><p>상수화 및 그룹화
일부 함수, 또는 리듀서와 같은 비슷한 변수나 함수를 생성하는경우, 그룹화를 통해 관리하는것이 좋습니다. 상수를 관리하는 객체의 경우, 이름또한 객체로 하는것이 좋다고 하셨습니다. 또한, 상수를 만들때는 타입값을 따로 명시적으로 적어주어 중복되는 상황을 막아야합니다.</p>
</li>
</ul>
<h3 id="bad-point">Bad Point</h3>
<ul>
<li><p>Commit의 내용부족
커밋의 제목만으로는 커밋의 변경내용을 표현하기 충분하지 않은 경우가 많습니다.
때문에 Commit은 해당 커밋에 대한 설명 또한 가능한 자세히 적어두는 습관이 필요합니다.</p>
</li>
<li><p>컴포넌트의 추상화
컴포넌트는 View만을 담당하는게 맞다고 생각합니다. 때문에 컴포넌트에서는 추상화를 가능한 많이 하는것이 맞다고 생각하였습니다. 예를들어 Dispatch의 사용, 3항 연산의 다수 사용 등으로, 컴포넌트의 본래 역할과 멀어진다면, 추상화를 생각해봐야 한다고 생각이 들었습니다.</p>
</li>
</ul>
<p>++ 추상화는 어렵기도 한 문제입니다. 이러한 부분은 확실한 정답이라 부를만한것도 없다고 생각합니다. 추상화는 많은 고민이 되는것이 맞는것이고, GitHub 등에서 각 년도 star를 가장 많이 받은 BestPractice 등을 따라 해보는것이 많은 도움이 될것이라 말씀하셨습니다.</p>
<h1 id="강의-section_2">강의 Section_2</h1>
<h2 id="첫번째-주제--렌더링">첫번째 주제 : 렌더링</h2>
<p>렌더링이란, <strong>화면에 특정한 요소를 그려내는것</strong>을 의미합니다.</p>
<p>기존의 바닐라 JS와 같은경우에는 DOM에 직접 접근하여 수정하는 방법을 채택하였습니다. 하지만 이러한 방식의 경우 DOM에 접근하는것이 많아지면 많아질수록 수정이 복잡해집니다. (명령형 방식)</p>
<p>때문에 이러한 복잡해지는 단점을 핵심UI만 관리하는 방식으로 복잡함을 보완해주고, 렌더링 또한 기존 방식보다  처리해주기 때문에 React와 같은 라이브러리를 사용하는것입니다.</p>
<p>일반적인 DOM의 렌더링 과정인 CRP(Critical Rendering Path)은 아래와 같습니다.</p>
<ol>
<li>HTML을 파싱해서 DOM(DOCUMENT OBJECT MDOEL)을 생성합니다.</li>
<li>CSS를 파싱해서 CSSOM(CSS OBJECT MDOEL)을 생성합니다.</li>
<li>DOM과 CSS를 결합하여 Render Tree를 생성합니다.</li>
<li>생성한 Render Tree와 Viewport를 사용하여 요소를 들의 위치와 크기를 계산합니다.(Layout 단계)</li>
<li>계산된 정보를 통해 render Tree의 정보를 실제 Pixel로 그려냅니다(Paint)</li>
</ol>
<p>이러한 과정은 DOM 또는 CSSOM이 수정될때마다 매번 반복됩니다. 적게 변하든 많이 변하든, 각각의 변경시마다 이러한 과정은 매번 반복이 되는것입니다.</p>
<p>이러한 단점을 보완하기 위하여, React는 가상돔(Virtual DOM)을 사용하였습니다.
가상돔의 사용이란, 간단히 말하여 기존 DOM의 값을 데이터상으로서 보관하고, 모든 변경 UI과정을 가상 DOM을 통해 먼저 계산합니다. 그리고 계산된 값을 실제 DOM에 넘겨주도록 설계되었습니다. </p>
<p>이러한 가상돔을 사용하기때문에, 기존 CRP를 통해 여러번 실행될 과정을 리액트는 1번만 실행할 수 있는것입니다.</p>
<p>리액트는 이러한 가상돔을 활용하기 위하여, 단순하지만 강제적인 규약이 정해져있습니다.
<strong>state가 변경되면, 무조건 리렌더링 한다.</strong>
이것이 리액트의 가장 기본적이지만, 핵심적인 명제입니다.</p>
<p>React는 우선 이벤트가 발생된다면 아래와 같은 방식이 시작됩니다.</p>
<ol>
<li>해당 컴포넌트에 변화가 없는지, 없다면 재사용할것인지 결정합니다. </li>
<li>state가 변경되면, 함수 컴포넌트를 호출합니다. </li>
<li>2의 결과를 통해서, 가상돔을 생성합니다</li>
<li>가상돔을 통해 모든 UI 계산값을 실제 DOM에 전달합니다.</li>
</ol>
<p>이러한 상황에서 React를 다루는 개발자가 할 수 있는 최적화는 몇가지로 단축됩니다. 우선적으로 기존 컴포넌트의 UI를 재사용할 수 있을것인지, 그리고 가상돔의 차이를 최대한 적게 하는것입니다. 이번 강의에서는 기존 컴포넌트의 재사용에 대하여 다루었습니다.</p>
<h2 id="두번째-주제--reactmemo">두번째 주제 : React.memo</h2>
<h3 id="reactmemo">React.memo</h3>
<p>React는 <strong>부모 컴포넌트의 state가 변할경우, 자식 컴포넌트 또한 모두 리렌더</strong>링 합니다.
하지만 부모 state가 변하더라도, 자식 컴포넌트에서 사용하는 state 및 상태,UI는 변하지 않는 경우도 있을것입니다. </p>
<p>그렇다면, 자식 컴포넌트의 props가 변하지 않았다면 자식 컴포넌트를 그대로 재사용하는것이 효율적일수도 있습니다. 이럴때 사용하는것이 React.memo입니다.</p>
<p>React.memo는 HOC(Higher Order Component)입니다.
HOC이란것은, 컴포넌트를 props로 받아 컴포넌트를 리턴하는 컴포넌트를 의미합니다.</p>
<p>React.memo를 사용한다면, 만약 <strong>자식 컴포넌트의 props가 변하지 않는다면, 해당 자식 컴포넌트는 리렌더링 되지 않습니다.</strong></p>
<p><code>React.memo는 2번째 props로 callback함수를 가지고 있습니다. 만약 함수의 리턴값이 true일 경우 무조건 이전 결과를 재사용하고, false일경우 리렌더링을 수행합니다.
기본값은 false입니다.</code></p>
<pre><code>const ReturnFalseMemo = React.memo(ChildComponent, () =&gt; false);
const ReturnTrueMemo = React.memo(ChildComponent, () =&gt; true);</code></pre><p>React에서 props의 변화를 감지한다는것은 기존의 개념과는 조금 다른 관점입니다.</p>
<h3 id="자바스크립트-데이터-타입">자바스크립트 데이터 타입</h3>
<p>설명에 앞서, 자바스크립트는 원시형과 참조형의 타입으로 나뉘어집니다. 원시형의 타입으로는 string,boolean,number,null 등의 값이 있고, 참조형이란 원시형을 제외한 타입. obj가 있습니다. obj에서 파생된 arr등도 참조형입니다.</p>
<p>원시형과 참조형의 가장 큰 타입은. 원시형의 데이터는 불변이라는것입니다.
원시형은 데이터가 할당되는 공간(주소)이 변하지 않습니다. 만약 도중에 값이 변경이 된다고 하더라도, 그것은 새로운 데이터 공간(주소)이 생겨나는것일 뿐입니다.</p>
<p>하지만 참조형의 경우는 다릅니다. 기본적으로 할당된 공간(주소)이 변하지 않습니다. 이는 참조형 타입인 객체를 유연하게 사용할수있게 하는데 도움이 되기도 하지만, 때때로  결과를 예측하게 힘들게 하기도 합니다. 각각의 객체는 다른 공간을 가지고 있기 때문에 값의 예측을 하기 위해선 객체의 
property를 모두 비교해줘야 합니다.</p>
<h3 id="불변성">불변성</h3>
<p>다시 React로 돌아와서, 리액트는 state가 변경되면 rerender를 실행시킵니다. 이때 기존의 모든 객체값을 그대로 <strong>불변</strong> 상태로 둔채, 새로운 객체를 생성합니다. 그리고 이것을 비교합니다. </p>
<p>React는 이러한 비교를 <strong>shallow compare(얕은 비교)</strong>를 이용하여 비교하고있습니다.
shallow compare란 객체의 모든 deep의 property를 일일히 비교 검사하진 않고, 첫 deep의 property를 비교한다는것을 의미합니다.</p>
<p>때문에, React의 최적화 함수를 사용할때는 자신이 제대로 최적화 함수를 사용하고 있는것인지 알고있어야 합니다.</p>
<h2 id="세번째-주제--memolizaition">세번째 주제 : Memolizaition</h2>
<p>리액트에서 사용되는 함수 컴포넌트는 기본적으로 함수입니다. 때문에 기본적으로 이전에 사용된 함수값과 새롭게 생성된 함수값은 공유될수 없습니다.</p>
<p>하지만 공유가 필요한 상황이 있을수도 있기 때문에, Reat는 몇가 API를 제공합니다.</p>
<h3 id="usememo를-사용">useMemo를 사용</h3>
<p>useMemo를 사용한다면, 기존의 &#39;값&#39;의 공간을 기억해둡니다. 그리고 그 값을 재사용하게 됩니다.
하지만 이러한 값은 저장한 값이 변경 되는 경우, useMemo의 값 또한 변경이 필요할수 있습니다. 때문에 첫번째 인자로는 저장할 값을 지정하고, 두번째 인자, 의존성 배열에는 해당 값이 변경될경우 다시 useMemo를 통해 값의 공간을 재할당 한다는 의미를 갖고 있습니다.</p>
<p><code>useMemo의 사용법</code>
<code>useMemo(() =&gt; computeExpensiveValue(a, b), [a, b]);</code></p>
<h3 id="usecallback을-사용">useCallBack을 사용</h3>
<p>react에서는 리렌더링시, 함수 컴포넌트내에 있는 함수 또 한 모두 재 생성됩니다. 이는 곧 React.memo등의 props 비교를 방해하게 됩니다. 때문에 useCallback을 사용하여 함수 컴포넌트에서 사용되는 함수의 공간값을 저장하여 재사용할 수 있습니다.
기본적으로 useMemo를 활용한 함수입니다. 
<code>아래의 두 함수는 같은 의미를 가지고 있습니다.</code></p>
<pre><code>const memorizedFunction = useMemo(() =&gt; () =&gt; console.log(&quot;Hello World&quot;), []);

const memorizedFunction = useCallback(() =&gt; console.log(&quot;Hello World&quot;), []);</code></pre><h3 id="memoization는-언제-해야할까">memoization는 언제 해야할까?</h3>
<p>메모이제이션 기능은 언뜻보면 효율적으로 보일수도 있습니다. props 변화가 없는 함수는 재활용을 한다면 가상돔의 계산도 적어질것이고, 이는 곧 성능의 향상이라고 생각할수도 있기 때문입니다.
하지만 최적화를 위한 과정 또한 리소스를 소모시킨다는것을 간과해서는 안됩니다. 메모이제이션 기능을 수행하기 위해선 우선, 값을 저장하고. 함수를 호출하고. 의존성을 비교한다. 이 3가지 과정을 거쳐야 합니다.
이 과정은 오히려 다루는 데이터가 적을 경우에는 불필요한 함수실행으로 인해, 성능적인 면에서 비효율적일수도 있는 부분입니다.</p>
<p>하지만 다루는 데이터가 많을경우, 예컨데 1만개에 가까운 데이터를 저장하는 컴포넌트를 가정한다면, 이 컴포넌트가 props의 변화가 없이 매 호출시마다 렌더링되는것은 매우 비효율적일것이고, 이러한 상황에서는 메모이제이션 기능이 매우 효율적일것입니다.</p>
<p>즉, 최적화란 만능적인 기능이 아니며, 상황에 따라 신중하게 접근해야 하는 부분입니다.</p>
<h2 id="최적화는-꼭-해야-하는걸까">최적화는 꼭 해야 하는걸까?</h2>
<p>최적화는 자동으로 되어지지 않습니다. 실제 개발자가 단순히 최적화 함수를 적어주는것으로는 최적화의 의미가 크게 없을 것입니다. 최적화 또한 많은 고민을 통해 결정하여야 되는 작업이기 때문입니다. 하지만 이러한 최적화는 실질적인 결과물을 나타내지는 않습니다. 기업은 체감이 크지 않은 최적화 보다는, 새로운 기능을 구현해내는 것을 선호할것입니다. 때문에 모든 작업물에 대하여 최적화를 해줘야 겠다는 생각은 좋지 않은 생각일수있습니다. 최적화가 필요한 시점은 대량의 데이터를 처리하여야 해서 UX적인 성능 증가가 필요한 시점등에서, 다른 구성원들의 동의를 얻어 작업을 진행하는것이 옳은 행동일것입니다.</p>
<h1 id="강의-section_3">강의 Section_3</h1>
<h2 id="첫번째-주제--useeffect의-의존성-배열">첫번째 주제 : useEffect의 의존성 배열</h2>
<h3 id="의존성-배열">의존성 배열</h3>
<p>useEffect에 감싸여 있는 함수는, 특정 상황에서 실행을 보장받습니다. useEffect는 빈 배열을 넘길경우 첫 실행시에만 작동하며, 아무것도 넘기지 않을경우 매 컴포넌트가 호출될때 실행되게 됩니다.</p>
<p><code>useEffect의 모습</code>
<code>useEffect(effect, 의존성)</code></p>
<p>또는, 의존성 배열에 추가적인 변수 및 함수들을 설정한다면, 의존성 배열에 들어있는 값이 변경될때 useEffect내부의 실행이 보장됩니다.</p>
<h3 id="의존성-배열을-잘-설정하는-방법">의존성 배열을 잘 설정하는 방법</h3>
<p>useEffect 함수를 잘 설정하기 위해선 분명, <strong>모든 의존하는 값을 의존성 배열에 명시해야 할것</strong>입니다.</p>
<p>하지만, 여기에 더해 <strong>가능한 의존성 배열을 적게</strong> 하는것 또한 버그가 발생하는 가능성을 줄여줄것입니다.</p>
<ol>
<li>첫번째 방법 : setState의 값을 함수형으로 변환해주기</li>
<li>두번째 방법 : 함수를 컴포넌트 밖으로 이동시키기</li>
<li>useCallback 또는 useMemo등을 활용한 메모이제이션을 통하여 값이 변경되지 않도록 설정하기</li>
</ol>
<p><code>useState의 set함수는 기본적으로 메모이제이션 되어있습니다. 때문에 의존성 배열에 들어가지 않습니다.</code></p>
<h2 id="두번째-주제--contextapi">두번째 주제 : contextAPI</h2>
<h3 id="context란">Context란?</h3>
<p>context란 맥락을 전달하는, React에서 제공하는 내장 API입니다.
많은 상황에서, 전역 값을 관리하는 형식으로 사용되기도 하지만, 그것만을 위해 만들어진 기능은 아닙니다.</p>
<p>기본적으로 React는 컴포넌트가 여러개의 자식 컴포넌트를 가지고 있습니다. 이러한 컴포넌트들의 deep가 깊어질수록 필요한 데이터를 넘겨주는데 많은 단계들을 거쳐야 할 수 있습니다. 이러한 맥락을 쉽게 넘겨주기 위해서 contextAPI는 구상되어졌습니다.</p>
<p>contextAPI를 통해 props를 관리하고, 전달하기 까다로울때 context를 통해 바로 값을 넘겨줄수있는것입니다.</p>
<p>contextAPI는 context를 필요로 하는 컴포넌트에서 부분적으로만 사용될수있습니다.</p>
<h3 id="사용법">사용법</h3>
<ol>
<li><p>createContext를 사용하여 context 객체를 생성합니다.
<code>const fooContext = createContext(defaultValue)</code></p>
</li>
<li><p>context에 있는 값을 전달하기 위해서는 Provider 컴포넌트를 이용해야 합니다. 이 값이 지정되어 있지 않으면, context 생성시 설정된 기본값이 제공됩니다. </p>
</li>
</ol>
<pre><code>const state = {foo:&quot;bar&quot;}
&lt;fooContext.Provider = {state}&gt;
&lt;Child&gt;
&lt;/fooContext.Provider&gt;</code></pre><ol start="3">
<li>useContext를 사용하여 해당 context의 Provider에 설정한 값을 불러 사용할수있습니다.</li>
</ol>
<p><code>const foo = useContext(fooContext)</code></p>
<h1 id="마무리-소감">마무리 소감</h1>
<p>리액트를 꽤 공부하였다고 생각하엿는데도, 이번 강의는 새로웠습니다. 개발을 하다보면 기존에 하던 방식의 개발방식에 고착되고, 때문에 다른 방식을 찾게되는 사고방식이 굳게 되는 느낌이 없잖아 있었는데, 이러한 수업을 통해서 기존의 코드에서 조금 더 좋게 사용될 수 있었던 부분을 발견할 수 있게되고, 기억에 잘 남아 있지 않던 개념 부분등, 많은 것을 배울 수 있었습니다.</p>
<p>또한, 강사님의 피드백이나 타인의 코드를 통해서도 얻어가는 점이 있는 느낌이 좋았습니다. loading이라는 변수보다는 is를 붙이거나, if문에서 사용되는 의미가 있는 변수값이면 상수로 표현하는 방법 등, 단순하지만 생각지 못했던 부분을 생각하고 이후에 쉽게 적용할수있을것 같다는 부분도 좋았습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WANTED] ASSIGNMENT_2]]></title>
            <link>https://velog.io/@jun_53/WANTED-ASSIGNMENT2</link>
            <guid>https://velog.io/@jun_53/WANTED-ASSIGNMENT2</guid>
            <pubDate>Tue, 01 Nov 2022 09:57:13 GMT</pubDate>
            <description><![CDATA[<h1 id="assignment_2">ASSIGNMENT_2</h1>
<h2 id="두번째-과제의-요구사항">두번째 과제의 요구사항</h2>
<p>두번째 과제의 요구사항은 대략적으로 이러하였습니다.</p>
<ol>
<li>GitAPI를 사용하여 정보를 가져온다</li>
<li>List를 사용하여 목록을 보여준다</li>
<li>정보를 가져올때 Error,Loading에 대한 처리가 이뤄져야한다.</li>
<li>List는 무한스크롤을 사용하여 계속하여 보여준다.</li>
<li>List의 정보를 클릭시 상세페이지로 이동한다</li>
<li>상세 페이지의 정보는 마크다운 문법으로 되어있는데, 이것을 CSS로 가공 하여 보여준다.</li>
<li>모든 페이지는 반응형이어야 한다.</li>
</ol>
<ul>
<li>과제 수행 시간 토요일 오후 3시~ 월요일 오전 9시 (약 1.5일)</li>
</ul>
<h2 id="시작하기에-앞서">시작하기에 앞서</h2>
<h3 id="초기-셋팅">초기 셋팅</h3>
<p>초기 셋팅은 첫번째 과제물과 유사하게 진행하였습니다.
불필요한 파일이 제거된 CRA를 생성하고,
ESLint,Prettier,husky가 설정이 된 Repo를 main이 올린 뒤, 팀원들이 모두 클론하여 각자의 브런치를 만들고 진행하였습니다.</p>
<h3 id="코딩-컨벤션">코딩 컨벤션</h3>
<p>추가적으로 이번 과제물에서는 자체적으로 코딩 컨벤션을 정하고 진행해보자는 의견이 있어 컨벤션을 추가해보았습니다. 지정한 컨벤션은 아래와 같습니다.</p>
<ul>
<li>컴포넌트의 ID사용은 지양한다.</li>
<li>react의 state는 여러개 사용시 최소 집합을 찾아 사용한다.</li>
<li>컴포넌트의 이벤트에서 불필요한 익명함수를 사용하지 않는다. (예시: 함수의 인자가 event 하나인 경우)</li>
<li>코드를 설명하는 주석은 가급적 사용하지 않는다.</li>
<li>상수는 영문 대문자 스네이크 표기법(Snake case)를 사용한다.(예시: SYMBOLIC_CONSTANTS)</li>
<li>반환 값이 불린인 함수는 &#39;is&#39;로 시작한다</li>
<li>const와 let은 사용 시점에 선언 및 할당한다.</li>
<li>함수는 사용 전에 선언해야 하며, 함수 선언문은 변수 선언문 다음에 오도록 한다.</li>
<li>이벤트 핸들러는 &#39;on&#39;으로 시작한다.</li>
<li>한 줄짜리 블록일 경우라도 {}를 생략하지 않으며 명확히 줄 바꿈 하여 사용한다.<ul>
<li>한 줄짜리 블록일 경우 {}를 생략할 수 있지만, 이는 코드 구조를 애매하게 만든다. 당장은 두 줄을 줄일 수 있겠지만 이후 오류 발생 확률이 높아 잠재된 위험 요소가 된다.</li>
<li>단, map과 같은 화살표 함수의 암시적 반환은 허용한다.</li>
</ul>
</li>
</ul>
<p>여기에 더해 </p>
<ul>
<li>return문 바로 위는 한 칸 비워 놓는다
라는 컨벤션도 있었지만, 프리티어와 충돌 되는 부분이 있어 도중에 제외 처리되었습니다.</li>
</ul>
<h2 id="bestpractice를-만들기-위해선">BestPractice를 만들기 위해선?</h2>
<h3 id="문제점">문제점</h3>
<p>과제물 제출 구현 시간이 어찌보면 매우 짧은 편이였습니다.
토요일 2시 30분쯤에 시작하여, 월요일 12시이전 제출, 자는 시간을 제외하면 결과적으로 일요일 자정까지는 완성이 되어야 하는, 하루 반나절정도의 시간이 주어졌습니다.</p>
<h3 id="해결책">해결책</h3>
<p>이런 상황에서 저희 팀은 우선적으로, 각자가 기능을 구현 한 후. git Repo의 Discusions을 제출 전날 자정 9시까지, 
각자의 요구사항에 맞는 기능구현 방법을 적어두고, 어느분의 방법이 좋다고 생각하는지 각자가 comments를 달아 의견을 표시하기로 하였습니다.
이러한 방법을 통해 중요 로직만을 코드리뷰 할 수 있도록 하고, BestPractice의 선정을 조금 더 효율적이게 할 수 있도록 하엿습니다.
선정은 코드리뷰를 통해 누구의 코드가 가장 이상적인지를 투표를 통해 선정하였습니다.</p>
<h3 id="아쉬운-점">아쉬운 점</h3>
<p>사실 이방법은 엄연하게는 BestPractice를 뽑는 방법이라 생각치는 않습니다. 왜냐하면 어느분의 코드에서 A라는 부분이 좋았고, 다른분 코드에서는 B라는 코드가 좋았는데. 이 두 부분을 적절히 잘 조정하여 코드를 만들어내는것이 BestPractice에 가까운 방법이라고 생각하였습니다.</p>
<p>하지만 각자의 코드 스타일과 구현방법이 다르며, 이 부분을 조정하여 리팩토링 하는데는 추가적인 시간이 들어갑니다. 또한 이러한 방식으로 같은 방식의 코드를 구현한다면 그것은 협업이 되고, 원티드 과제물의 기본적인 원칙인 개인 과제물과는 어울리지 않는다 생각하였습니다.</p>
<p>하지만 그럼에도, 좋은 점의 일부라도 가져와서 리팩토링을 적은 시간이나마 시간을 할당하여 하는것이 맞다 생각하였습니다.</p>
<h2 id="진행">진행</h2>
<p>기본적인 진행방식은 아래와 같았습니다.</p>
<p>토의 진행 -&gt; 진행 결과 Discusions을 공지 -&gt; 기능구현 -&gt; 토의 진행... 
의 반복이였습니다.</p>
<p>이전 과제와는 달리 Discusions을 적극적으로 활용하였습니다. 
Discusions을 토의 결과를 정리하는것과, 자신의 기능구현을 설명하는것. 
이렇게 두가지 용도로 사용하였습니다.</p>
<p>토의 에는 는 구현 방법에 대한 논의, 코드 구조 및 기타 개발 정보공유 등이 이루어졌습니다.</p>
<h4 id="토의-디스커션-예시">토의 디스커션 예시</h4>
<p><img src="https://velog.velcdn.com/images/jun_53/post/b6896448-a6bc-4943-bad9-f7a81324eb52/image.JPG" alt="">    </p>
<h4 id="기능구현-설명-디스커션-예시">기능구현 설명 디스커션 예시</h4>
<p><img src="https://velog.velcdn.com/images/jun_53/post/0439e29a-2c3e-4c52-8167-fb252dac4bab/image.JPG" alt=""></p>
<h2 id="코딩구현">코딩구현</h2>
<p>개인적인 구현 과정에서 막혔던 부분의 List는 아래와 같았습니다.</p>
<ol>
<li>Context를 사용하여 어떻게 구현할것인지</li>
<li>인피니티 스크롤의 구현방법은?</li>
<li>useEffect가 두번 실행되는 이유는?</li>
<li>마크다운문법을 어떻게 css로 표현할까?</li>
<li>컴포넌트화는 어느정도로 어떻게 해야할까</li>
<li>반응형은 어느정도로 이루어져야되는걸까</li>
</ol>
<h3 id="1-context를-사용하여-어떻게-구현할것인지">1. Context를 사용하여 어떻게 구현할것인지</h3>
<p>이부분은 loading과 error를 어떻게 같이 구현해야할지 고민을 좀 하다, 이전에 공부하였던 velopert 개발자님의 깃북을 참고하여 거의 대부분 따라하였습니다.</p>
<p>하지만, 기존 state값을 저장하는 부분은 적혀있지 않았기때문에, 약간의 코드를 변형하여 사용하였습니다. </p>
<pre><code>// 리듀서 부분
if (list) {
    return {
            ...state,
            [key]: asyncState.listSuccess(state[key], action.payload),
          };
        }
// 사용되는 state
  listSuccess: (state, payload) =&gt; {
    return {
      loading: false,
      data: state.data ? state.data.concat(payload) : payload,
      error: null,
    };
  },</code></pre><h4 id="아쉬운-부분">아쉬운 부분</h4>
<ul>
<li>스프레드 연산자를 사용하였다면 조금 더 좋았을것 같습니다 </li>
<li>코드가 다른사람이 보기엔 좀 불편하지 않았을까 싶습니다.</li>
</ul>
<h3 id="2-인피니티-스크롤의-구현방법은">2. 인피니티 스크롤의 구현방법은?</h3>
<p>인피니티 스크롤의 구현방법 또한, 팀원이 공유해주신 방법을 따라 했습니다.
공유된 페이지에서는 useRef,useEffect,Interaction Observer를 사용한 구현이 적혀있었는데.</p>
<p>약간 미완성인채로 적혀있었습니다. useRef값을 useEffect에서 의존성 배열에 넣어줘야 하는데, 이것을 넣어주지 않아 초기에 값이 undefined가 표시되어 정상적인 실행이 되지 않았습니다.
때문에, useRef 대신 useState값을 useRef와 유사하게 사용하였습니다.</p>
<pre><code>  useEffect(() =&gt; {
    const option = {
      threshold: 0,
    };
    const observer = new IntersectionObserver(onHandleObserver, option);

    if (target) {
      observer.observe(target);
    }

    return () =&gt; {
      observer &amp;&amp; observer.disconnect();
    };
  }, [target, onHandleObserver]);</code></pre><p>하지만 이 방법은 페이지가 리렌더링 될때마다 state 값 또한 재할당되고, 쓸데없이 리렌더링 되는 방법으로서 좋은 방법이 아니라고 생각합니다.
결과적으로, <strong>useRef의 값을 useEffect의 의존성 배열에 넣으면 해결되는 문제</strong>였습니다.</p>
<h4 id="아쉬운-부분-1">아쉬운 부분</h4>
<ul>
<li>useEffect의 의존성 배열을 미처 생각하지 못했습니다.</li>
</ul>
<h3 id="3useeffect가-두번-실행되는-이유는">3.useEffect가 두번 실행되는 이유는?</h3>
<p>List를 구현하며 느낀점은, 첫 렌더링시 useEffect 함수가 두번 실행된다는것입니다. 이 부분에 대해 왜 그런지 검색해보니 CRA로 생성된 리액트 프로젝트에서는
React.StrictMode가 기본적으로 적용이 되어있는데, 이것은 오류를 잘 찾아내기 위해 기본적으로 2번의 렌더링을 거치게 되고, 이로인해 useEffect가 두번 실행되었습니다.</p>
<p> <strong>index.js에 있는 &lt;React.StrictMode&gt; 태그를 제거 하였습니다.</strong></p>
<h3 id="4-마크다운문법을-어떻게-css로-표현할까">4. 마크다운문법을 어떻게 css로 표현할까?</h3>
<p>gitHub의 글은 대부분 마크다운으로 이루어집니다. 그래서 정보를 가져올때 컨텐츠 값이 마크다운일 경우가 많은데. 이것을 CSS로 변환을 어떻게 해야하는지에 대한 문제가 있었습니다.</p>
<p><strong>찾아낸 해결법</strong></p>
<ul>
<li>marked 라는 Lib를 설치하여 마크다운 문법으로 이루어진 컨텐츠를 적용하여 html 언어로 변형합니다.</li>
<li>이렇게 변형한 html을 react의 dangerouslySetInnerHTML의 기능을 이용하여 강제적으로 html에 innerHTML을 하게 합니다.</li>
<li>여기서, 저는 tailwind를 사용하고 있었기 때문에 CSS가 정상적으로 표현되지 않았습니다. 때문에 추가적으로 &#39;@tailwindcss/typography&#39; Lib를 설치-이용하여 변형된 html을 자동으로 CSS를 꾸며주도록 하였습니다</li>
</ul>
<p>최종적인 코드</p>
<pre><code>          마크다운 문법 변환
      const markdown = marked(body);

          // tailwind의 typo 기능을 구현하기 위하여  &lt;article className=&quot;prose&gt; 적용
        &lt;article className=&quot;prose prose-stone sm:w-[578px] w-screen&quot;&gt;
        {&lt;div dangerouslySetInnerHTML={{ __html: markdown }}&gt;&lt;/div&gt;}
      &lt;/article&gt;
</code></pre><p>해당 로직을 통해 css처리화된 마크다운 컨텐츠
<img src="https://velog.velcdn.com/images/jun_53/post/72978548-cbca-4baf-a787-77c87eed0492/image.JPG" alt=""></p>
<h3 id="5-컴포넌트화는-어느정도로-어떻게-해야할까">5. 컴포넌트화는 어느정도로 어떻게 해야할까</h3>
<p>많은 분들의 과제물 결과를 보면, 컴포넌트를 작은 단위로 세분화 해서 만들었다고 느꼈습니다.</p>
<p>하지만 제 방식의 경우, 컴포넌트화는 정말 큰 레이아웃으로서 재활용되는 부분만 컴포넌트화를 진행하였습니다. 그 편이 개발 방식도 편하고, 컴포넌트를 너무 세분화 하면 보기 불편하지 않을까 하는 생각이였습니다.</p>
<p>사실 이부분은 아직도 애매모호한 부분이라고 생각되고 있습니다. 더욱 공부하고 노력해야 될 부분이라고 생각합니다.</p>
<p>그래도 이번 과제에서 적용된 부분은 일부있습니다.</p>
<ul>
<li>컴포넌트의 큰 부분에 대해서 styled-componet를 적용하여 조금 더 보기 편하고, 유지보수가 쉽도록 변경</li>
<li>공통 Layout을 만들고, Outlet을 활용하여 라우터에 적용</li>
</ul>
<h4 id="아쉬운-부분-2">아쉬운 부분</h4>
<ul>
<li>컴포넌트화가 일부는 styled-componet를 적용시키지 않고, tailwind그대로 사용하는 등 통일화되지 않은점</li>
<li>더 많은 부분에서 세분화가 가능했음에도 큰 레이아웃으로서만 컴포넌트화를 진행한 점</li>
</ul>
<h3 id="6-반응형은-어느정도로-이루어져야되는걸까">6. 반응형은 어느정도로 이루어져야되는걸까</h3>
<p>tailwind CSS를 사용하는경우 큰 설정없이 반응형이 이루어진다고 생각하고 있었습니다. flex와 w-screen 등을 사용하면 자동적으로 반응형이 이루어지니까요.</p>
<p>하지만 약간의 착각이였습니다.
min-w 같은 옵션도 그냥 적당히 580px정도 주면 되겠지 하고 PC화면으로만 테스트 해봤는데, 모바일 환경에서는 깨져보이고, 실제 핸드폰으로 접속햇을경우는 더욱 안좋게 보이는 구나 라는 경험을 하였습니다.</p>
<p>때문에 반응형을 사용하는경우, 뷰 포트와 퍼센트 css를 사용할때 min-w 값과 기본 w값을 주는것에 대해서 신중하게 접근해야 한다는걸 알았습니다.</p>
<p><strong>구현방법</strong></p>
<ul>
<li>가능 하면 full 또는 screen값을 지정해주었습니다.</li>
<li>sm:w-[578px] 와 같이, sm(680px) 사이즈 이상일경우에만 px을 직접적으로 지정해주었습니다.</li>
</ul>
<h4 id="아쉬운-부분-3">아쉬운 부분</h4>
<ul>
<li>Dark모드를 사용할 경우 모든 레이아웃에 다 같이 적용되어야 합니다. 일부만 적용되어 디자인이 매우 이상하게 되었습니다.</li>
<li>w 옵션과 h 옵션을 어느부분은 px단위로 주고, 어느부분은 full로 주어서 정확한 반응형이 이루어 지지 않았습니다.</li>
<li>pc같은 경우 스크롤 영역때문에 ui가 일부 먹혀들어가 좋지 않은 UX가 돼었습니다다. 이부분의 해결책을 찾지 못하고 제출하였습니다.</li>
</ul>
<h2 id="배포-링크">배포 링크</h2>
<p>(작업 기한 1.5일)
<a href="http://wanted-pre-onboarding-june.s3-website.ap-northeast-2.amazonaws.com/">http://wanted-pre-onboarding-june.s3-website.ap-northeast-2.amazonaws.com/</a></p>
<h2 id="git-repo">Git Repo</h2>
<p><a href="https://github.com/jun-05/wanted_assignment_02">https://github.com/jun-05/wanted_assignment_02</a></p>
<h2 id="bestpractice-선정-결과">BestPractice 선정 결과</h2>
<p>Discusions을 통하여 의견을 나누고, 최종적으로 선정된 분의 과제물을 최종 과제물로 제출하였습니다. 이분의 코드에서 좋다고 생각하였던 부분은 아래와 같습니다.</p>
<ol>
<li>폴더구조가 관심사에 맞게 잘 분리되어있다.</li>
<li>gitMsg와 Msg의 설명이 깔끔히 잘 되어있었다.</li>
<li>코딩 구현 자체도 깔끔히 잘 하셨다</li>
<li>요구사항을 모두 충족하셨고, 반응형도 깨짐 없이 잘 되어있었다.</li>
<li>컨텍스트에서 스프레드 연산자를 사용하셨는데, 이부분을 저는 생각 못했던점</li>
<li>마찬가지로 무한스크롤을 useEffect가 아니라 함수로 만들어서 사용하신부분도 배울만한 점이였습니다.</li>
</ol>
<p>사실 저는 막히는 부분은 검색하여 대부분 약간만 변형하여 구현하였는데, 이런식으로 변형하여 코딩을 구현하는점은 많이 배워야 한다고 생각했습니다.</p>
<p>선정 결과 링크(비공개)</p>
<h2 id="진행-중-아쉬웠던-점">진행 중 아쉬웠던 점</h2>
<p>코딩 부분 </p>
<ul>
<li>스스로 검색에 의존한 코딩개발을 하였던것 같다고 느껴 아쉬웠습니다.</li>
<li>컴포넌트화를 어느정도 할지, 그리고 어느부분은 styled-componet가 진행되고, 어느 부분은 tailwind가 날것 그대로 있고, 전체적인 마무리가 아쉬웠습니다.</li>
<li>.env 파일을 .gitignore에 추가하는것을 깜빡했습니다.</li>
<li>context에서 state를 관리할때. concat대신 스프레드 연산자를 사용하는것을 생각하지 못한게 아쉬웠습니다.</li>
<li>생각해보면 당연한 버그인데, 한참 고민했던점(무한 스크롤 부분)</li>
<li>미처 수정하지 못했던 버그들.. (글자가 길어지면 레이아웃이 살짝 아래로 내려감, pc화면에서 width를 최소로 하면 스크롤 영역때문에 레이아웃이 살짝 짤림 등..)</li>
</ul>
<p>팀 진행 부분 </p>
<ul>
<li>아직 BestPractice를 뽑는 방식의 최적점을 찾지 못한게 아쉬웠습니다.</li>
<li>첫 토의때는 기록보다는 구두 소통을 진행하였습니다. 때문에 기록을 제대로 남기지 못한 점이 아쉬웠습니다.<ul>
<li>때문에 토의가 진행될수록 기록을 남길 수 있도록 노력하였습니다.</li>
</ul>
</li>
<li>Discusions에 기능구현에 대한 설명을 통일되지 않은 양식으로 하다보니 비교 및 보는게 불편했던것 같습니다.</li>
<li>과제물에 제출할 readMe를 매 과제마다 작성해야 하는 점 등이 불편할것 같다 생각했습니다.<ul>
<li>때문에 Discusions을과 readMe에 대한 대략적인 템플릿을 만들어 둬야 한다 생각하였습니다.</li>
</ul>
</li>
</ul>
<h2 id="과제물-진행-중-좋았던-점">과제물 진행 중 좋았던 점</h2>
<p>개인 과제 기준입니다.</p>
<ul>
<li>짧은시간 몰입하여 프로덕션을 개발하는 경험을 하였습니다.</li>
<li>진행 할 수록 진행을 어떤식으로 해야 발전할수있는지에 대한 경험을 하였습니다.</li>
<li>코드의 품질이 조금씩 발전되고 있다는 경험을 하였습니다. </li>
<li>git commit과 코딩 컨벤션이 추가 되어, 코딩에 규칙이 생긴점이 좋았습니다.</li>
<li>이전 과제물과 달리 tailwind-styled-componet를 사용하여 컴포넌트를 조금 더 보기 쉽게 정리하였던 점이 좋았습니다.</li>
<li>gitMsg를 어떻게 활용해야 할지 알게 되었습니다.</li>
<li>tailwind의 typograpy 기능과 marked라는 라이브러리를 새롭게 익히고 사용해본 경험을 통해, 다음에 마크다운 언어를 어떻게 다뤄야 할지 배우게 되었습니다.</li>
<li>동료 학습을 통해, 내가 맞다고 생각한 부분이 틀릴수도 있다는것을 알게 되었습니다.</li>
<li>CI/CD를 통해 배포 자동화를 경험해 보았습니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WANTED] 프리온보딩 코스 week 1-2]]></title>
            <link>https://velog.io/@jun_53/WANTED-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%BD%94%EC%8A%A4-week-1-2</link>
            <guid>https://velog.io/@jun_53/WANTED-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%BD%94%EC%8A%A4-week-1-2</guid>
            <pubDate>Tue, 01 Nov 2022 07:56:11 GMT</pubDate>
            <description><![CDATA[<h1 id="강의-section_1">강의 section_1</h1>
<p>두번째 강의의 첫번째 섹션에선
첫번째 과제물에 대한 피드백이 있었습니다.
장점과 단점에 대한 예시와 이유를 함께 강의주제 였습니다.</p>
<p>(해당 게시글은 실제 강의 내용을 그대로 옮겨적은것은 아니며, 주관적인 이해 및 판단으로 요약되어진 글입니다.)</p>
<h2 id="첫번째-주제--과제-리뷰">첫번째 주제 : 과제 리뷰</h2>
<h3 id="good-point">Good Point</h3>
<ul>
<li><p>commit Convetion</p>
<ul>
<li>협업, 또는 팀 단위로 개발을 할때는 공통된 commit 제목 규약을 가지고 하는것이 좋습니다.</li>
<li>참고로 한 레퍼런스가 있다면, 같이 올려두는것도 좋습니다.</li>
</ul>
</li>
<li><p>idSelector는 지양</p>
<ul>
<li>react의 컴포넌트는 매우 자주 렌더링이 됩니다. react의 특성상 고유값이여야 하는 id는 매 렌더링시마다 값이 중복으로 사용되기 때문에 사용을 지양하여야 합니다.</li>
</ul>
</li>
<li><p>로직의 분리 (응집도를 높이자)</p>
<ul>
<li>연관된 기능끼리 뭉쳐져 있는 코드는 다른사람이 코드를 볼때 읽기 쉽게 합니다.</li>
<li>유지보수가 필요로 한 경우, 해당 영역에서만 처리하면 되기때문에 유지보수가 수월해집니다.</li>
</ul>
</li>
</ul>
<h3 id="bad-point">Bad Point</h3>
<ul>
<li><p>react state의 남용</p>
<ul>
<li>react의 state는 값이 변경되면 페이지가 리렌더링 됩니다. 이런 특성이 있기때문에, state가 바뀌면 UI가 바뀌는 경우에만 사용되어야 합니다.</li>
<li>때문에 state의 사용은 가능한 최소한으로 사용하는것이 좋습니다. state의 남용을 막기 위해서는 아래와 같은것을 생각해보는것이 좋습니다.<ul>
<li>부모로부터 props를 받은값을 다시 한번 state를 사용하진 않았는지</li>
<li>시간이 지나도 변하지 않는 값이 state로 사용되고 있지는 않은지</li>
<li>컴포넌트 안의 state나 props가 bool값인 경우 공통 분모를 통하여 최적화 시킬순 없는지</li>
<li>단순히 어떠한 값의 bool값이 필요한 경우라면 이중부정 !! 을 사용하여 최적화 할순 없는지</li>
</ul>
</li>
</ul>
</li>
<li><p>불필요하면 익명함수 지양</p>
<ul>
<li>jsx의 onEvet 함수는 기본적으로 evt 인자를 넘겨주기때문에, 사용하고자 하는 함수가 evt인자 또는 인자를 필요로 하지 않는경우는 익명함수를 사용하지 않는것이 좋습니다.</li>
</ul>
</li>
<li><p>주석은 필요한 경우만 사용</p>
<ul>
<li>코드를 설명하는 주석은 최대한 지양해야합니다. <ul>
<li>나중에 코드가 수정될경우, 주석 또한 수정되어야 하는 번거로움이 있을수있고, 수정되지 않고 방치된 주석은 타인에게 혼란을 줄 수 있습니다.</li>
</ul>
</li>
<li>코드로 설명이 불가능한 주석은 사용되면 좋습니다.<ul>
<li>TODO 주석의 사용 : 이슈가 있어서 완성되진 못했지만, 나중에 수정을 해야 할 경우 사용되는 주석</li>
<li>FIXME 주석의 사용 : 지금은 처리 하지 못하지만, 나중에 처리해야 할 이슈를 적을 때 사용하는 주석</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1 id="강의-section_2">강의 section_2</h1>
<p>두번째 섹션에서는 서버에 대한 강의가 있었습니다.</p>
<h2 id="두번째-주제--서버">두번째 주제 : 서버</h2>
<p>서버란, <strong>무언가를 제공해주는 컴퓨터</strong> 입니다. </p>
<p>웹 환경에 있어 서버란, 프로그램 실행환경에서 네트워크를 통해 서버에 접근요청이 오면, 정해진 요청에 따라 특정 리소르를 응답합니다.</p>
<p>때문에 서버는 웹 환경에 있어, 없으면 안되는 필수적인 하드웨어적인 환경입니다.</p>
<p>이러한 서버를 관리하는 크게 두가지로 나뉘어져 왔습니다.</p>
<h3 id="온-프레미스-환경">온 프레미스 환경</h3>
<p>온 프레미스란, 서버를 서비스하는 회사에서 직접 관리하는 환경을 의미합니다. </p>
<ul>
<li><p>장점 </p>
<ul>
<li>회사가 직접 서버를 관리하기 때문에, 소프트웨어는 물론, 물리적인 환경까지 온전히 통제할 수 있습니다.  </li>
<li>때문에 중요한 환경에서는 온 프레미스 환경을 채택하는 경우가 많습니다.</li>
</ul>
</li>
<li><p>단점</p>
<ul>
<li><p>서버는 물리적인 실체이기때문에, 실제 제반 공간, 시설, 인력 등이 필요로 하며, 꾸준히 관리해야 하는 필요성이 있습니다. 즉, 초기 설치 비용이 비싸며, 유지보수 비용을 지속적으로 필요로 합니다.</p>
<ul>
<li>설치된 서버의 수용량은 이후 변경이 어렵기 때문에, 순간적으로 많은 수용이 필요로 할 경우 정상적인 작동의 기대가 어려울 수 있습니다.</li>
</ul>
</li>
<li><p>최근의 데이터센터 화재사고, 또는 기타 천재지변과 같이 예기치 못한 사건에 대해서는 취약할 수 있습니다.</p>
</li>
</ul>
</li>
</ul>
<h3 id="클라우드-컴퓨팅-환경">클라우드 컴퓨팅 환경</h3>
<p>클라우드 컴퓨팅이란, 직접적으로 서버를 구비하지않고, 클라우드 서비스를 이용하여 서버를 작동시키는 환경을 의미합니다.</p>
<ul>
<li><p>장점 </p>
<ul>
<li>고객이 직접 물리적인 서버를 운영 할 필요가 없습니다</li>
<li>원하는 사양, 원하는 시간을 직접 조정할 수 있습니다.(이것이 가능한 이유는 클라우드 서비스는 여러개의 컴퓨터를 연결하여 하나의 컴퓨터 같이 연산 가능하게 하여 제공하기 때문입니다.)</li>
<li>지역에 크게 구애받지 않습니다.<ul>
<li>만약 유럽,아메리카에서도 서비스를 시작하고자 할때, 그 지역의 클라우드 서버를 사용 한다면 원활한 운영이 가능합니다.</li>
</ul>
</li>
</ul>
</li>
<li><p>단점</p>
<ul>
<li>온 프레미스의 장점의 정 반대입니다. 고객측은 클라우드 서비스의 통제에 관여할수 없기때문에, 서비스가 원활하게 이루어지기를 기대할 수 밖에 없습니다.</li>
</ul>
</li>
</ul>
<h3 id="클라우드-컴퓨팅의-분류">클라우드 컴퓨팅의 분류</h3>
<p>클라우드 컴퓨팅은 IaaS , Paas , SaaS 와 같은 3계층의 형태로 나뉘어질 수 있습니다.</p>
<ul>
<li><p>IaaS(Infrastructure as a Service)</p>
<ul>
<li>클라우드 서비스의 가장 기본적인 형태입니다. </li>
<li>설정되어 있지 않은 컴퓨터를 대여 받는 수준입니다.</li>
<li>때문에 사용자는 많은 부분을 직접 구성, 관리를 해줘야 하는 단점이 있습니다.</li>
<li>대표적인 서비스는 AWS의 EC2가 있습니다.</li>
</ul>
</li>
<li><p>PaaS(Platform as a Service)</p>
<ul>
<li>구성 요소들을 플랫폼화 해서 제공해주는 형태입니다.</li>
<li>소프트웨어의 운영 및 관리를 PaaS에 위임하기 때문에 개발자들은 효율적인 개발을 할 수 있습니다.</li>
<li>단점으로는, PaaS는 IaaS에 비해, 플랫폼에 종속적인면이 강해지며, 접근이 허용되지 않아 제어가 안되는 문제, IaaS보다는 높은 비용문제 등의 단점이 있습니다.</li>
<li>대표적인 서비스로는 Heroku,Github Pages 등이 있습니다.</li>
</ul>
</li>
<li><p>SasS(Software as a Service)</p>
<ul>
<li>고객이 바로서비스를 사용할 수 있게, 소프트웨어 까지 모두 제공되어지는 형태입니다.</li>
<li>클라우드 서비스를 통해 서비스가 제공되기때문에, 고객이 PC에 직접 파일을 설치할 필요가 없거나, 고객의 컴퓨팅 사양이 중요치 않은 경우가 많습니다.</li>
<li>대표적인 서비스로는 Slack,NetFlix 등이 있습니다.</li>
</ul>
</li>
</ul>
<h3 id="aws">AWS</h3>
<p>AWS는 클라우드 컴퓨팅 서비스를 제공하는 프로바이더로, 현재 전세계에서 가장 많이 사용되어지는 클라우드 컴퓨팅 서비스입니다.
AWS는 단순히 컴퓨팅 자원의 제공 뿐만 아니라, 이를 편리하게 관리 할 수 있도록 도와주는 여러 서비스를 제공하며 높은 안정성, 확장성, 보안성 등을 가지고 있습니다.</p>
<p>이번 섹션에서 다루어본 주제는 S3 였습니다.</p>
<p><strong>S3는</strong> Simple Storage Service의 약자로서, 단순한 형태의 <strong>파일 보관소</strong>라고 생각 할 수 있습니다.
특정 파일, 이미지 등을 저장하고 인터넷상으로 접근할수있게 하는 서비스로도 사용될 수 있지만,
CRA를 통하며 빌드된 파일, index.html은 정적인 파일이며, 렌더링이 CSR로 작동한다는 특징이 있어, 이를 이용하여 S3 서비스를 이용해서 웹 페이지를 배포할 수 있습니다.</p>
<p><strong>S3를 이용하는 방법</strong></p>
<p>가장 기본적인 사용법 입니다.</p>
<ol>
<li><p>AWS의 S3로 접근하여 버킷을 생성합니다.</p>
<ul>
<li>버킷의 이름은 고유하여야 하며, 서비스 지역을 설정하고, 버킷의 퍼블릭 액세스 차단 설정을 해제하여야 합니다.</li>
</ul>
</li>
<li><p>생성된 버킷의 속성 TAB 에서 하단의 웹 사이트 호스팅 기능을 활성화 합니다. </p>
<ul>
<li>이때 인덱스 문서, 에러페이지 문서는 index.html로 동일하게 합니다.</li>
</ul>
</li>
<li><p>권한 탭의 버킷정책 설정에서 사용하고자 하는 정책을 설정합니다.</p>
<ul>
<li><p>객체에 대한 접근 권한을 json형식으로 작성할수있습니다.</p>
</li>
<li><p>많이 사용되어지는 형식의 정책은 아래와 같습니다.</p>
<pre><code>{
&quot;Version&quot;: &quot;2012-10-17&quot;,
&quot;Statement&quot;: [
  {
      &quot;Sid&quot;: &quot;&lt;name&gt;&quot;,
      &quot;Effect&quot;: &quot;Allow&quot;,
      &quot;Principal&quot;: &quot;*&quot;,
      &quot;Action&quot;: &quot;s3:GetObject&quot;,
      &quot;Resource&quot;: &quot;arn:aws:s3:::&lt;버킷 네임&gt;/*&quot;
  }
]
}</code></pre><ul>
<li>version은 사용하고자 하는 정책 버전을 말하며</li>
<li>Statement에서 직접적으로 사용하고자 하는 정책을 설정합니다. </li>
<li>Sid는 만드는 정책의 이름을 의미하며</li>
<li>Effect는 허용여부를 의미합니다.</li>
<li>Principal은 누구에게 허용할 것인지를 의미하며, &quot;*&quot;은 모두가 접근 할 수 있는것을 의미합니다.</li>
<li>Action은 무엇을 할것인지를 의미하며, 위와 같은 설정에서는 s3의 오브젝트를 가져가는것을 허용할것을 말합니다.</li>
<li>Resource는 무엇을 가져갈것인지를 말하며, 위와 같이 <strong>버킷네임/*</strong> 로 설정 될 경우 모든 파일을 가져갈수 있다는것을 의미합니다.  </li>
</ul>
</li>
</ul>
</li>
</ol>
<ol start="4">
<li>위와 같은 설정 이후, 객체 탭으로 이동하여, 자신이 오나성한 CRA의 build를 통해 정적인 html파일을 만들고, build 파일안의 파일들을 모두 객체 폴더에 업로드 합니다.</li>
</ol>
<ol start="5">
<li>속성 탭의 권한 웹 사이트 호스팅에 생성되어 잇는 url을 통해 완성한 CRA의 페이지로 이동이 가능하게 됩니다.</li>
</ol>
<h1 id="강의-section_3">강의 section_3</h1>
<p>섹션 3에서는 CI/CD에 대해 다루어보았습니다.</p>
<h2 id="세번째-주제--cicd">세번째 주제 : CI/CD</h2>
<h3 id="cicd의-의미">CI/CD의 의미</h3>
<p>CI/CD란  Continuous Integration(CI)와 Continuous Delivery/Deployment(CD)를 통합해서 부르는 용어입니다.</p>
<p>CI/CD를 이용한다는것은 개발과정에서 필요한 빌드,테스트,배포 등의 과정을 자동화 한다는것입니다.</p>
<p>CI의 관심사는 코드를 지속적으로 통합해 나가는것을 의미합니다. 중요한것은 에러가 발생하지 않는지 체크해주는것. 머지 후에도 제대로 관리가 되어질 수 있을까 라는 것. 유효한 코드인지 검사해주는 것. 이러한 것들에 대한 체크를 CI는 자동화 해준 다는 것입니다.</p>
<p>CD의 관심사는 CI과정을 통과한 코드를, 실제 사용자가 사용하는 Production 환경에 배포하는것이며, CD는 이러한 과정을 자동화하는것입니다.</p>
<p>즉, CI/CD 과정이란, CI에서 코드에 문제가 없는지를 검사하며, CD에서 CI를 통과한 코드를 배포하는 과정을 자동화 한다는것을 의미합니다.</p>
<h3 id="cicd-플랫폼의-종류와-사용-이유">CI/CD 플랫폼의 종류와 사용 이유</h3>
<p><strong>1. 클라우드 플랫폼의 종류</strong></p>
<p> CI/CD의 사용은 파이프라인을 구축하여 사용합니다. 파이프라인을 구축하고, 사용하기 위해서는 CI/CD 플랫폼을 사용하게되는데, 이러한 CI/CD플랫폼 또 한 2가지 분류로 나뉘게 됩니다. </p>
<p> 크게 설치형과 클라우드 형으로 나뉘게 되는데,
설치형으로는 Jekins가 대표적이며, 클라우드형으로는 GitHub Actions 등이 있습니다.</p>
<p> 설치형의 장점으로는 온-프로미스 서버와 유사하게, 실행환경의 통제가 가능하다는 장점이 있습니다.</p>
<p> 클라우드형의 장점으로는 별도의 컴퓨팅 자원이 필요 없이, 서비스 제공자가 모두 운영해준다는 장점이 있습니다.
하지만 플랫폼에서 제공해주는 수준까지만 사용 가능하기 때문에, 세부적인 조정이 불가능 하다는 단점이 있습니다.</p>
<p><strong>2. 사용 이유</strong></p>
<p> 현재의 개발자는 상대적으로 많은 업무를 맡고 있습니다. 개발자는 기본적인 개발 이외에도, 고객의 피드백에 대해서 빨리, 그리고 자주 개선해야 하는 등의 추가적인 업무 등, 맡고 있는 업무가 하드웨어적인 개발환경보다 많이 있습니다.</p>
<p> 이러한 환경에서 개발자는 CI/CD를 통해 코드의 유효성 검사와 배포를 자동화를 맡김으로서, CI/CD를 위한 시간 할애를 하지않고, 본연의 개발업무에 집중할 수 있게 해줍니다, 또한 해당 과정에서 오류가 발생할시 자동적으로 개발자에게 이를 알려주는 기능 또한 가지고 있기때문에 CI/CD는 개발환경을 효율적으로 나아갈 수 있도록 도와줍니다.</p>
<h3 id="github-action를-사용한-cicd">GitHub Action를 사용한 CI/CD</h3>
<p><strong>1.GitHub Action을 사용하는 장점</strong></p>
<ul>
<li>클라우드 형이라 관리가 쉽습니다.</li>
<li>GitHub Repo와의 연동이 쉽습니다.</li>
<li>Repo안에서 CI/CD까지 함께 구축하고 관리할수있다는 이점</li>
<li>타 CI/CD 플랫폼 보다 초기 러닝커브가 낮다는 점</li>
<li>다른 사람이 만들어둔 좋은 설정들이 공유되어 있고, 쉽게 사용이 가능하다는 점</li>
</ul>
<p>__2.사용방법 __</p>
<ol>
<li>Repo안에서 CI/CD를 자동화 하기 위해선 프로젝트 최상위 .github/workflows/ 경로에 cicd.yml 을 작성하여야 합니다. 이는 일종의 규약입니다.</li>
<li>CI/CD를 위한 파이프라인을 작성합니다. 아래는 CI/CD 통해 테스르를 자동화하고, AWS에 배포까지 하는 과정을 담은 파이프 라인입니다.</li>
</ol>
<pre><code> name: &lt;NAME&gt;

on:
  push:
    branches:
    - &lt;Branch&gt;
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@master
    - run: npm ci
    - run: npm run test
    - run: npm run build
    - name: deploy to s3
      uses: jakejarvis/s3-sync-action@master
      with:
        args: --delete
      env:
        AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}
        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        AWS_REGION: &#39;ap-northeast-2&#39;
        SOURCE_DIR: &#39;build&#39;</code></pre><ul>
<li><p>name은 워크플로우 이름입니다.</p>
</li>
<li><p>on은 언제 이벤트가 실행될것인지를 의미합니다.</p>
</li>
<li><p>push설정을 하여, push가 될경우 자동화 될 수 있도록 설정되었습니다.</p>
</li>
<li><p>branches는 어느 브랜치에 push될 경우 자동화 할것인지를 의미합니다. 기본값은 master로 되어 잇으며, 이경우에는 따로 설정할 필요가 엇습니다.</p>
</li>
<li><p>workflow_dispatch 는 GitHub Action Page에서도 수동으로 CI/CD를 할 수있게 하는 옵션입니다.</p>
</li>
<li><p>jobs는 실행할 작업 목록을 의미합니다. jobs는 여러개의 스텝을 가질 수 있습니다.</p>
</li>
<li><p>deploy 라고 명시되어 있는 부분은 jobs에서 실행할 첫번째 작업의 name입니다. 이것은 수동적으로 변경할수있습니다.</p>
</li>
<li><p>runs-on 은 git actions에서 제공하는 클라우드의 어느 환경에서 실행할것인지를 의미합니다.</p>
</li>
<li><p>steps는 어떠한 작업을 할것인지를 의미합니다</p>
</li>
<li><p>uses는 타인이 만들어둔 지정된 워크플로우를 사용한다는것을 의미합니다.</p>
</li>
<li><p>npm ci는 npm install과 유사하지만, cleanInstall을 의미하며, 완벽히 정확한 버전의 패키지만을 설치한다는 차이점이 있습니다. </p>
</li>
<li><p>이후 빌드까지의 작업이 이어집니다.</p>
</li>
<li><p>name은 하단에 이어질 step 파이프라인 작업의이름입니다.</p>
</li>
<li><p>uses를 통하여 jakejarvis/s3-sync-action@master의 파이프라인 설정을 가져와 사용하였음을 의미합니다.</p>
</li>
<li><p>with은 옵션으로서, aws는 파일이 추가되면 기존의 파일을 제거하는 설정을 가지고 잇습니다. 이것을 args: --delete로 표시합니다.</p>
</li>
<li><p>env는 step에서 사용할 환경설정입니다.</p>
</li>
<li><p>시크릿 정보들은 repo - setting - secret - action secrets 에서 환경값을 설정할수있습니다. 위 문서에서는 버킷,키 아이디, 액세스 키 등을 등록해야 합니다.</p>
</li>
<li><p>AWS_REGION 는 사용할 aws 클라우드 서비스의 위치를 의미합니다.</p>
</li>
<li><p>SOURCE_DIR 는 배포된 파일에서 어떤 dir 내부의 파일을 사용할것인지를 의미합니다.</p>
</li>
<li><p>AWS_SECRET_ACCESS_KEY와 같은 경우, AWS 설정을 터미널 환경에서도 사용하기 위해 발급받아야 하는 토큰값입니다.</p>
<ul>
<li>이를 발급받기 위해선 개인setting - 보안자격증명 - 액세스 키 부분에서 key를 발급받아야 합니다.</li>
</ul>
</li>
</ul>
<h1 id="마무리-소감">마무리 소감</h1>
<p>이번 강의에서는 저번 과제에 대한 피드백과 서버,CI/CD에 대해 공부하였습니다.</p>
<p>총 3시간내외 강의였는데, 정말 밀도있는 수업이라고 느꼈습니다.
몇번 따라해보기는 했지만, 두루뭉실하게 남아있던 클라우드 서비스, CI/CD에 대해서 이번 기회로 어느정도 확실한 개념과 사용법을 익힐 수 있었습니다.</p>
<p>사실 프론트엔드는 웹 VIEW 쪽만 잘 하면 되지 않을까라는 생각도 하긴 했엇는데, 강사님이 마지막에 말씀해주신 프론트엔드 개발자는 단순히 개발뿐만 아니라, 여러분야에 대해 협업을 해야하고, 또한 알고 있어야 하는게 필수불가결 하다는걸 강조하셨습니다.</p>
<p>특히, CI/CD와 같은경우, 프로덕트 배포에서 문제가 생겻을때 빠르게 문제가 발생했는지 확인하고 대응하는 능력이 있어야 하기 때문에, 중요도가 높다고 강조하신게 제 좁은 시야를 넓혀주신 느낌이였습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WANTED] ASSIGNMENT_1]]></title>
            <link>https://velog.io/@jun_53/WANTED-ASSIGNMENT1-3poarjb8</link>
            <guid>https://velog.io/@jun_53/WANTED-ASSIGNMENT1-3poarjb8</guid>
            <pubDate>Fri, 28 Oct 2022 16:16:11 GMT</pubDate>
            <description><![CDATA[<h1 id="assignment_1">ASSIGNMENT_1</h1>
<p>프리온보딩 코스는 매주 2개의 과제, 총 7개의 과제가 주어집니다.</p>
<p>첫번째 assignment는 랜덤으로 주어진 팀(7명)에서 BestPractice(모범사례)를 만드는것이였습니다.</p>
<h3 id="시작과정">시작과정</h3>
<p>정해진 과제물의 정답은 없었고. 이것을 어떻게 해석하고 만들것인지 또한 팀의 자율적인 해석을 통해 도출해내는 과제였습니다.
기본적으로 토론을 통해서 어떠한것이 모범사례인지 찾아야 하는 과정이 선행되어야 했습니다.</p>
<p>저희 팀은 그걸 위해 우선적으로 <strong>각자의 코드를 발표</strong>하는 시간을 가졌습니다.</p>
<p>그러한 과정을 통해서 <strong>각자의 코드에서 Good Point와 BadPoint를 발견</strong>하여 적어주기로 하였습니다.</p>
<p>과정을 마무리 한 후, 저희들이 <strong>BestPractice를 찾기 위해 제시한 제안</strong>은 두가지였습니다.</p>
<p>첫번째는 제가 제시했었던 찾아낸 포인트를 가지고 각자 리팩토링을 해보자, 그리고 점진적으로 개선해가면서 BestPractice를 선정하는게 어떻겠냐는 것이였습니다.</p>
<p>또 하나의 안은, Good Point들을 모아서 하나의 프로젝트를 새롭게 만들어보자는 것이였습니다.</p>
<p>이것에 대한 의견은 나뉘었고, 잠깐의 휴식시간을 거친후
다른 팀원분의 의견으로 기존 과제물의 요구사항을 들여다보고, 그것에 맞는 Good Point를 찾아 개선하는것이 중요한것 같다는 의견을 받았고, 첫번째 안과 결합하여 최종안으로 다른 팀원분들의 동의를 얻었습니다.</p>
<p>최종적으로 선정된 진행과정은 <strong>요구사항을 지켜내기 위한 GoodPoint라 생각하는 부분들을 모아, 다 같이 기존의 과제물을 리팩토링을 진행</strong>하고, 이후에 한분의 과제물을 선정하여 그것을 제출하자는 것이였습니다.</p>
<h3 id="시작전-규칙">시작전 규칙</h3>
<h3 id="선정된-요구사항">선정된 요구사항</h3>
<h4 id="1-폴더구조">1. 폴더구조</h4>
<p>이부분은 각자의 코드 스타일이 너무 강하게 작용하는 부분이였기 때문에, 강제하기엔 어렵다 생각해서 <strong>자율적인 선택</strong>에 맡기기로 하였습니다.</p>
<h4 id="2-로그인회원가입-경로-구분">2. 로그인/회원가입 경로 구분</h4>
<p>이부분 또한, 각자 구현된 CSS가 가지각색이여서 <strong>자율적인 선택</strong>으로 하였습니다.</p>
<h4 id="3-이메일-비밀번호-유효성-검사-기능">3. 이메일, 비밀번호 유효성 검사 기능</h4>
<p>이 기능은 어느분은 파일로 빼서 함수를 구현하였고, 어느분은 컴포넌트 내부에서 구현하였는데, 함수의 단일책임 원칙과 의존성 최소화, 유지보수 면에서 함수를 따로 빼서 보관하는것이 좋다 생각하여 다같이 <strong>utils함수에서 정규식 함수를 만들기로</strong> 하였습니다. </p>
<h4 id="3-2-위-조건-만족할때만-버튼-활성화">3-2. 위 조건 만족할때만 버튼 활성화</h4>
<p>이 기능은 모두 조건 만족하였지만, 추가적으로 버튼자체는 disabled라도, html상에서 지울수도 있고 또한 함수의 유지보수 측면에서 만약이라도 에러가 날 가능성도 있을수 있기때문에 <strong>버튼 클릭시 한번 더 조건을 체크하는 함수를 실행</strong>시키자고 하였습니다.</p>
<h4 id="4-로그인-api-호출-성공시-todo-이동">4. 로그인 API 호출 성공시 /todo 이동</h4>
<p>모든 분들이 promise의 status값이 200일시 이동하는걸로 하였습니다. 하지만 추가적으로 200이 아닌 값, <strong>ErrorStatus 또한 관리하는것을 권장</strong>하였습니다. </p>
<h4 id="5-jwt-토큰값-로컬-스토리지에-저장">5. JWT 토큰값 로컬 스토리지에 저장</h4>
<p>3번과 동일한 이유로 <strong>utils함수에서 토큰값을 관리하는 함수를 만들기로</strong> 하였습니다.</p>
<h4 id="6-로그인-여부에-따른-리다이렉트-처리">6. 로그인 여부에 따른 리다이렉트 처리</h4>
<p>몇몇 분의 <strong>라우터에서 함수를 사용</strong>하여 로그인 여부에따라서, pages를 분기처리하는 함수를 사용하는 방법이 인상적이여서, 이러한 부분을 따르기로 하였습니다. </p>
<h4 id="7-투두-리스트-목록-todo-접속시">7. 투두 리스트 목록 (/todo 접속시)</h4>
<p>useEffect에서 바로 값을 불러오는것보단, 명시적으로 값을 받아오는 함수, getTodos와 같은 <strong>함수를 생성한후 useEffect내부에서 사용</strong>하는것으로 하였습니다.</p>
<h4 id="8-투두-내용-및-완료-여부-표시">8. 투두 내용 및 완료 여부 표시</h4>
<p>조건자체는 모든 분이 만족하였습니다.</p>
<h4 id="9-입력추가-버튼-추가-버튼-클릭시-입력창-내용-투두-리스트에-추가">9. 입력/추가 버튼, 추가 버튼 클릭시 입력창 내용 투두 리스트에 추가</h4>
<p><strong>공백에서는 투두를 입력,수정할 수 없도록 하기로 하였고, 에러가 있을시 에러 표시</strong> 나타내도록 하였습니다.</p>
<h4 id="10-투두리스트-수정-버튼-클릭시-수정모드-활성화-및-투두-리스트-내용-수정-가능">10. 투두리스트 수정 버튼 클릭시 수정모드 활성화 및 투두 리스트 내용 수정 가능</h4>
<p>이부분은 많은 분들이 기본적인 조건은 만족하셨지만, 추가적으로 각자의 생각에 맞게 기능을 구현한 부분이 있기 때문에 <strong>내용과 완료여부를 수정하는 조건을 수정모드에서 다 같이 관리</strong>하는것이 좋다는 의견이 <strong>권장</strong>되었습니다.</p>
<h4 id="11-수정모드시-우측에-제출취소-버튼-표시">11. 수정모드시 우측에 제출/취소 버튼 표시</h4>
<p>모든 분이 조건을 만족하였습니다.</p>
<h4 id="12-개별아이템-우측에-삭제-버튼">12. 개별아이템 우측에 삭제 버튼</h4>
<p>모든 분이 조건을 만족하였습니다.</p>
<h4 id="13-그외-기능옵션">13. 그외 기능(옵션)</h4>
<p>팀원분들 각자가 독특한 추가 기능들을 구현해주셨습니다. 
괜찮은 추가 기능들이 있었고, 이부분을 구현하는것은 각자의 선택으로 하였습니다.</p>
<ul>
<li>modal로 메시지 관리</li>
<li>errMsg를 status에 따라 관리하기</li>
<li>로그아웃 기능</li>
<li>페이지네이션</li>
<li>카운터 기능 or 완료 / 미완료 필터 기능<h4 id="14-기타-규약">14. 기타 규약</h4>
추가적으로 팀 전체의 코드에서 지키고자 하는 규약을 정하였습니다.</li>
<li>컴포넌트나,컨텍스트 등 내부에서 크게 상관없는 관심사 밖의 함수는 utils등의 파일에 보관할것</li>
<li>변수명 data는 지양하고,가능한 명확한 변수명을 사용할것</li>
<li>resetCSS가 적용이 안되어있다면 크로스 브라우징을 위하여 사용할것</li>
<li>CRA에서 필요없는 파일은 삭제할것</li>
<li>프로젝트는 불필요한 CRA가 삭제되었고, ESLint,prettier 설정을 한 husky가 설치된 프로젝트를 클론하여 각자의 과제물을 리팩토링할것</li>
<li>아래와 같은 커밋 규칙을 추가하여 진행하였습니다.<pre><code>⭐ feat : 새로운 기능에 대한 커밋
</code></pre></li>
</ul>
<p>🛠 fix : 버그 수정에 대한 커밋</p>
<p>🧱 build : 빌드 관련 파일 수정에 대한 커밋</p>
<p>👏 chore : 그 외 자잘한 수정에 대한 커밋</p>
<p>⚒ refactor :  코드 리팩토링에 대한 커밋</p>
<p>🎨 style : 코드 스타일 혹은 포맷 등에 관한 커밋</p>
<p>✏ docs : 문서 수정에 대한 커밋</p>
<p>💡 ci : CI관련 설정 수정에 대한 커밋</p>
<p>```</p>
<h3 id="결과">결과</h3>
<p>이러한 리팩토링 과정을 거치고, 이후 투표를 거쳐서 한분의 프로젝트를 선정하였습니다.</p>
<p>최종적으로 선정된 분의 코드는 토의에서 대부분의 <strong>요구사항을 만족하였고, 코드 퀼리티, 컴포넌트 관심사 분리등 모든 부분에서 가장 깔끔하다 생각되신분의 과제물</strong>로 제출하게 되었습니다.</p>
<h3 id="개인적-소감">개인적 소감</h3>
<p>정말 반성이 많이 되는 첫번째 과제물이였습니다.
개인적으로 제 코드 실력에 대한 반성이 가장 컸던것 같습니다.
과제물 자체도 정말 딱 최저라인에 맞게 작성하였고, 추가 기능이나 코드의 퀼리티를 생각하면 정말 처참하다 생각이 들었습니다.</p>
<p>정말 아쉬웠던 부분은, CSS부분에서 tailwind를 사용했는데, 이것을 사용하다보니 className이 너무 길어졌고, 그러다보니 component화를 하는것에 부담을 느꼈습니다. 때문에 관심사를 분리한 컴포넌트는 거의 최소한으로 하였고, 이부분은 정말 잘하신분 과는 많은 차이를 느꼈습니다.</p>
<p>두번째로 axios에서의 err처리를 저는 하지 않았습니다. 요구사항에 없었기 때문이기도 하지만, 이후에 구현해보려 했는데도 생각만큼 효율적이게 구현되지 않는 실력을 체감하며 반성을 하였습니다.</p>
<p>세번째로 요구사항에 있던 라우터에서 함수를 생성하여 처리하는 부분에서 저는 결국 구현에 막혀서 하지 못한 부분입니다. context를 사용하여 login을 구분하였고, tempLogin으로 새로고침시 다시금 login 상태를 입력하였는데 이부분에서 충돌이 일어났고, 좋은 방법이 생각나지 않아 구현하지 못하였습니다.</p>
<p>네번째로 readME부분에서, 많은 분들이 정말 보기 좋게 요약을 해주셨는데 저는 정말 무성의하게 작성하거나, 그마저도 하지 않았었다는걸 깨달았습니다.</p>
<p>마지막으로, 본의 아니게 팀의 팀장을 맡게 되었습니다.
사실 원친않은 직이였습니다. 성격상 맡는 역할도 아니였습니다.
하지만 어쩌다보니 맡게되었고, 어쨌거나 팀의 진행을 이끌어야 하는 역할과 책임을 맡게된것 같았습니다.</p>
<p>그러다 보니 괜히 호응을 얻기 위한 쓸데없는 말도 많이 하였던것 같고, 제 코딩 실력자체도 좋지 않은편인데, 앞장서서 의견을 말하려다 보니 잘못된 의견이나 제 일방적인 의견을 제안하기도 하였던것 같습니다.</p>
<p>다행히 팀원분들의 많은 도움으로 좋은 의견을 내주셔서 과제가 좋은 방향으로 진행되기도 하였고, 어느 한분이 노션과 과제 제출물의 리드미등을 잘 꾸며주시기도 하셨습니다.</p>
<h4 id="개선점">개선점</h4>
<p>우선, 팀장의 역을 맡은 것에 대한 앞으로의 개선점을 말하자면
혼자서 너무 많은것을 하려 할 필요가 없다는것을 느꼈습니다. 그렇기 때문에 저는 필요한 방향으로의 말만 하면서 진행해나가면 되겠다는 생각을 하게되었습니다. </p>
<p>코드 부분에서의 개선은 우선 tailwind를 사용하고 있지만, twin.macro라는 테일윈드용 라이브러리와 tailwind-styled-component라는 라이브러리의 사용을 고민하다, 후자를 선택하여 앞으로는 가능하면 많은 부분을 관심사를 분리한 컴포넌트화를 진행하기로 하였습니다.</p>
<p>나머지 부분은 시간이 촉박하기때문에 단기간에 고쳐지진 않을것입니다. 하지만 남은 기간동안 적어도 민폐는 되지 않도록, 최선을 다하자는 생각으로 코드 실력을 발전시키고자 하였습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WANTED] 프리온보딩 코스 week 1-1]]></title>
            <link>https://velog.io/@jun_53/WANTED-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%BD%94%EC%8A%A4-week-1-1</link>
            <guid>https://velog.io/@jun_53/WANTED-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%BD%94%EC%8A%A4-week-1-1</guid>
            <pubDate>Fri, 28 Oct 2022 13:56:28 GMT</pubDate>
            <description><![CDATA[<h2 id="1-프리온보딩이란">1. 프리온보딩이란?</h2>
<p><img src="https://velog.velcdn.com/images/jun_53/post/3c09aec2-5a60-481e-85f0-46a974e80838/image.JPG" alt="">
                                <a href="https://www.wanted.jobs/events/pre_ob_fe_7">https://www.wanted.jobs/events/pre_ob_fe_7</a></p>
<p>프리온보딩이란, HR기업인 원티드에서 일정 수준이상의 구직자와, 또한 구직자가 원하는 회사를 매칭 해주기 위하여 서로의 조건을 만족시켜줄 수 있도록 준비된 코스입니다.</p>
<p>코스 과정은 4주간 진행되며, 준비된 총 6번의 기업과제물을 받고, 실제로 구현하여 제출하는 과정을 거치게 됩니다.
프리 온보딩은 pre + Onboading의 직관적인 단어 조합으로,
사전에 조직문화,업무에 빠르게 적응할수 있도록 돕는 과정을 의미하기도 합니다.</p>
<p>매 과정은 개인 과제로서 구현하고 제출되지만, 동시에 팀 과제로서 모범사례 하나를 뽑아 제출하는 형식인것 같습니다.</p>
<h2 id="2-참가-이유">2. 참가 이유</h2>
<p>저는 대부분의 경우 독학으로 웹개발을 공부하였고, 그것이 득이 될 경우도 있었지만, 사실 대부분의 경우에는 좋지 않았던것 같습니다. 코드 리뷰 또한 받을 수 없었고, 협업의 경우에도 결국 프론트엔드는 저 혼자밖에 없어서 다른 사람과 실질적으로 협업하는 경험도 전무하였습니다. 
또한, 취업을 위해서 정말 이런저런 많은 것들을 건들여봤지만, 저에게 정말 필요한게 무엇인지 모른채 많은 시간을 허비해왔던것 같다라는 생각이 들던차에 원티드 프리온보딩 코스를 발견하게 되었습니다.</p>
<p>실제로 내가 기업의 기대에 만족하는 결과물을 낼수있을까?? 그럴 수 있는 실력이 있는걸까? 사실 이것이 가장 프리온보딩 코스에서 경험해보고 싶었던 부분이였습니다. 때문에 프리온보딩 코스를 지원하였습니다.</p>
<h2 id="3-첫-번째-강의">3. 첫 번째 강의</h2>
<p>사실 프리온보딩을 지원하게 된 이유는 기업과제 이외에도, 커리큘럼에 대해 많은 흥미를 가지고 있었기 때문이기도 합니다.
개발 코드를 공부해오고는 있었지만, 이것이 맞는 방식인지. 실제로는 어떠한 방식으로 많이들 사용하고있는지, 또한 왜 그렇게 사용되고 있는지에 대해 관심이 있었는데. 프리온보딩에서 다루는 과정은 하나같이 모두 흥미를 끌만한 주제였습니다.</p>
<p>주 2회 강의를 진행하게 되는데, 첫 번째 강의는 간단한 오리엔 테이션, 참가 과제물에 대한 피드백, 그리고 <strong>Git,ESLint,Prettier 및 husky</strong>를 통한 자동화를 다루었습니다.</p>
<h3 id="3-1-오리엔테이션">3-1 오리엔테이션</h3>
<p>오리엔테이션의 앞부분에서는 기본적으로 과제 수행을 위한 지식 및 도움 제공을 해주지 않으며, 정답을 정해주고, 주입하는 교육이 아니라는것을 공지하였습니다.
기본적으로 프리온보딩이 추구하는 가치는 &quot;<strong>동료 학습을 통해 지식을 공유하고 토론하는 과정을 통한 역량 향상</strong>&quot;
그리고 커리큘럼에서 가르치고자 하는것은 <strong>시야가 확장되는 것</strong> 을 목표로 하는 과정이 될것이라 하였습니다.</p>
<p>주니어에게 있어 가장 중요한것은 현재의 기술 보다는, 성장 가능성이 더 중요하다는 것이며, 지식들 보다는, 왜 이걸 사용하는지, 적용하면 뭐가 좋은지에 대한 <strong>why</strong>에 대한 강조를 하셨습니다.</p>
<p>그저 독학을 통해서 주어진 지식을 습득만 해온 저에게 이런 발언 및 경험은 신선하고, 또한 어찌보면 낯설기때문에 어려울수있는 과정일 수도 있겠구나 라는 생각이 들었습니다. 하지만 동시에 이 과정을 견디고 적응한다면 분명히 좋은 결과가 있을것 같다라는 생각이 들었습니다.</p>
<h3 id="3-2-과제-피드백">3-2 과제 피드백</h3>
<p>선발과제에 대한 전체적인 피드백을 해주셨는데,
여기에서 중점으로 말씀해주신것중 기억에 남는것은 이런거였습니다.
기본적으로 보는건 readME,commitMsg,배포 또는 영상여부
그리고 추가적으로 파악하고자 하는건 코드의 스타일, 프로젝트 구조, 과제 수행태도, git활용 도 등을 언급하셨습니다.</p>
<p>과정 중 생각나는 조언들은, 컴포넌트의 가독성에 대해 언급해주셨는데
기본적으로 컴포넌트는 &quot;논리적인 단위&quot; 로서 잘 분해되어 사용되었는가를 의미하고,
의존성은 어떻게 설계되어있는지, 외부의 변화에 유연하게 대응할 수있는지에 대해서 말씀해주신것 들이였습니다.</p>
<p>사실 위와 같은 것들은 기본적으로도 중요한 것들이지만, 구직자 입장에서 중요한 이유는
이것들은 채용 담당자의 관심사를 끄는 주요 issue이기 때문입니다.</p>
<p><strong>부실한 readMe, 관리되지 않은 gitMsg, 규칙성 없는 코드 포멧팅, 기능구현을 확인하기 어려운 과제, 허가되지 않은 라이브러리 사용</strong> 는 채용자 입장에서는 극악과 같은 것이였고</p>
<p>구직자는 기본적으로 이러한 사항들을 지양하며, 좋은 모습으로 관리하여 구직자에게 좋은 인상을 일차적으로 남겨야 합니다. 또한 그러한 관심이 그 사람에 대한 추가적인 관심까지 이어질수있어야 합니다. 그사람의 GitPage, 블로그 등과 같이 관심을 가질 수 있게하는 요소가 있어야 한다고 말씀하셨습니다.</p>
<p>이러한 부분은 제가 생각은 하고 있었지만, 잘 지키지 않고 있던 부분이였는데, 자세하게 설명해주시고, 충분히 납득가능하고 반성 할 수 있는 기회가 되었습니다.</p>
<h3 id="3-3-개발자의-기본기-giteslintprettiergithook">3-3 개발자의 기본기 (Git,ESlint,prettier,gitHook)</h3>
<h4 id="첫번째-git에-대한-개념-gitmsg의-중요성">첫번째: Git에 대한 개념, GitMsg의 중요성</h4>
<p>Git은 분산 버전 관리시스템, 약간 풀어말하자면 코드의 버전 관리를 도와주는 시스템이며, GitHub는 Git의 원격 저장소입니다.</p>
<p>Git 자체만으로는 그저 로컬환경에서 버전을 관리하는것에 불과하지만, GitHub를 통해서 개인적인 Git을 타인과 공유하고, 팀원과 공동으로 작업할수있는 환경이 갖추어집니다.</p>
<p>GitHub와 같은 공동 작업 환경이 갖추어진 상황에서는, Git을 다루는것은 신중하여야 합니다. 혼자서 개발하는것이 아닌, 여러명과 개발하기때문에 이전 버전으로 되돌아 가야되는 상화잉 올수있고, 이전 버전에 대한 정보를 확인 해야 되는 과정이 있을수도있습니다.
이러한 과정에서 GitMsg가 제대로 작성되어 있지 않거나, 무성의하게 작성되어 있다면, 버전 관리가 매우 힘들고 복잡해질것입니다.
이러한 것을 위해서 commit시 Msg를 사용하는데, 이 Msg 규칙 또한 각자 다르게, 또는 같은 언어라도 다른 형식으로 사용되어진다면 이 또한 이전 상황과 크게 다르지 않을것입니다.</p>
<p>때문에 GitHub 환경에서는 <strong>통일된 GitMsg</strong> 를 사용하는것이 매우 중요하다 볼 수 있습니다.</p>
<p>추가적으로 알려주신것은, 불가피하게 작업중에 Commit을 하게 되는 과정이 있을수도있는데, 이러한 과정은 나중에 commit이 뒤죽박죽으로 섞여있게 하는 원인이 될 수 있기때문에, 이러한것은 git Squash를 사용하여 관리 할수 있다 말씀하셨습니다.
참고링크 : <a href="https://www.delftstack.com/ko/howto/git/git-squash-commits/">https://www.delftstack.com/ko/howto/git/git-squash-commits/</a></p>
<h4 id="두번째-eslintprettierhusky">두번째: ESLint,Prettier,Husky</h4>
<p>사실 ESLint,Prettier는 기존에도 사용하고 있던것들이였고, 중요성에 대한것은 많이 들어왔던것이였습니다.</p>
<p>기본적으로 개발자들은 각자의 코딩 스타일은 가지고있기때문에, 다른 스타일은 가진 개발자는 타인의 코드를 보기 어려울수있습니다.
때문에 협업을 하는 관계에서는 이러한 부분을 통일하기 위해 JS를 다루는 개발자들에서는 ESLint로 지양하는 문법을 어느정도 제한하고
Prettier를 사용하여 코드의 스타일은 교정하는데 사용하고있습니다.</p>
<p>대략적으로 위와 같은것들에 대한 설명, 그리고 설정에 대한 설명, 그리고 충돌을 막기 위한 라이브러리(eslint-config-prettier), 이전에 했던 정보는 다시 할 필요가 없으니 --cache, 그리고 .eslintcache는 ignore 파일에 추가해줘야 된다는것과 같은 추가설정 그외 설명이 이루어졌습니다.</p>
<p>마지막으로 이러한 작업이 반드시 지켜지도록 하는, GitHook을 사용하는 <strong>husky</strong> 에 대한 설명이 있었습니다.</p>
<p>husky는 GitHook을 사용하는데, 쉽게 설명하면 commit과 push 이전에 ESLint와 Prettier의 실행을 보장하는 라이브러리입니다.</p>
<p>husky의 사용법은 아래와 같습니다.</p>
<ol>
<li><p>npm install husky --save-dev</p>
</li>
<li><p>npx husky install (처음 husky 세팅하는 사람만 실행)</p>
</li>
<li><p>npx husky install (husky에 등록된 hook을 실제 .git에 적용시키기 위한 스크립트)</p>
</li>
<li><p>package.json에 &#39;husky install&#39;설정 (clone 받은 사람이 이후 간단히 설정할 수 있기 위하여)</p>
</li>
</ol>
<pre><code>    // package.json

    {
      &quot;scripts&quot;: {
        &quot;postinstall&quot;: &quot;husky install&quot;
      },
    }</code></pre><ol start="5">
<li><p>ESLint,Prettier의 scripts 설정</p>
<pre><code class="language-json"> // package.json

 {
   &quot;scripts&quot;: {
     &quot;postinstall&quot;: &quot;husky install&quot;,
         &quot;format&quot;: &quot;prettier --cache --write .&quot;,
         &quot;lint&quot;: &quot;eslint --cache .&quot;,
   },
 }</code></pre>
</li>
<li><p>add pre-commit, pre-push hook</p>
<ol>
<li><code>npx husky add .husky/pre-commit &quot;npm run format&quot;</code></li>
<li><code>npx husky add .husky/pre-push &quot;npm run lint&quot;</code></li>
</ol>
</li>
</ol>
<p>이 과정을 통하여 husky를 사용하여 ESLint와 Prettier의 자동화를 보장 할 수있게 됩니다.</p>
<h2 id="마무리">마무리</h2>
<p>사실 첫 강의에서는 오레인테이션식으로 많은 것이 나오지 않지 않을까 생각했었는데, 오리엔테이션부터 husky까지 모든 강의가 모두 유익하고 배울점이 있는 강의였습니다.</p>
<p>저는 개발자의 기본소양이라고 말씀하신 readME와 GitMsg, Git페이지의 소홀이 매우 심각한 상태였었고, 블로그 또한 관리하지 않았었는데. 이러한 부분에선 다시금 문제를 깨달을 수 있었습니다.
또한 동료학습을 통하여 문제는 스스로 해결해야 한다는 신조 또한 4주간의 경험으로 배울 수 있을것이라 기대하고있습니다.</p>
<p>그리고 husky 를 통한 코드 포맷팅에 강제성 및 자동화 또한 강의의 목적이고자 했던 <strong>시야가 확장되는</strong> 것을 조금이지만 맛을 볼 수 있었던것 같습니다.</p>
<p>전체적으로 만족했던 첫 수업이였고, 앞으로가 매우 걱정되지만 기대되기도 하는 4주일것 같습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[카우치코딩] 6주차 진행 회고]]></title>
            <link>https://velog.io/@jun_53/%EC%B9%B4%EC%9A%B0%EC%B9%98%EC%BD%94%EB%94%A9-6%EC%A3%BC%EC%B0%A8-%EC%A7%84%ED%96%89-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@jun_53/%EC%B9%B4%EC%9A%B0%EC%B9%98%EC%BD%94%EB%94%A9-6%EC%A3%BC%EC%B0%A8-%EC%A7%84%ED%96%89-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 25 Apr 2022 10:49:43 GMT</pubDate>
            <description><![CDATA[<h2 id="6주차-진행">6주차 진행</h2>
<ul>
<li>댓글의 CRUD 및 별점 기능을 구현하였다.</li>
<li>게시물의 평균 별점 및 좋아요 기능을 구현하였다.</li>
<li>로딩창을 구현하였다</li>
<li>마이페이지 구현중</li>
</ul>
<h3 id="댓글-구현">댓글 구현</h3>
<p>댓글의 경우는 기능을 어떤것까지 넣을것인지, 구현을 어떻게 할것인지 고민을 많이 했던것 같다. CSS는 댓글을 구현할때쯤에 이제 flex 레이아웃을 어떻게 구현해야 하는지 감을 많이 잡게 되었다. 이제서야 서먹서먹했던 css와 친구가 된 느낌이였다.</p>
<p>기능의 경우에는 처음에는 이미지까지 처리할까 하다, 여유가 없을것 같아서 폐기 하였고, 구현의 경우에는 입력하는 텍스트의값을 리덕스로 뺄까, 컴포넌트의 state로 뺄까 고민하다 굳이 전역으로 둘 필요는 없을것 같다 컴포넌트위 state로 빼는걸로 하였다. 지나고보면 왜 그런 고민을 했는지 잘 기억이 나지 않는것 같다.
댓글의 페이징 기능은 우선순위가 높지 않고, 여유롭게 진행중인 상황이 아니여서 구현하지 않았다.</p>
<pre><code class="language-javascript">  const onPublish = useCallback(
    async ({ reviewId, content, reviewRating, image }) =&gt; {
      try {
        if (reviewId) {
          await updateReview({
            reviewId,
            content,
            placeId,
            reviewRating,
            image,
          });
          dispatch(
            updateReviewState({ reviewId, content, reviewRating, image }),
          );
        } else {
          await writeReview({ content, placeId, reviewRating, image });
          dispatch(readReview({ placeId }));
        }
      } catch (e) {
        console.log(e);
      }
    },

    [dispatch, placeId],
  );
// 리듀서내 액션 update와 remove 
    [UPDATE_REVIEWS]: (
      state,
      { payload: { reviewId, content, reviewRating, image } },
    ) =&gt; ({
      ...state,
      reviews: state.reviews.map((item) =&gt;
        item.id === reviewId
          ? { ...item, content: content, reviewRating, image }
          : item,
      ),
    }),
    [REMOVE_REVIEWS]: (state, { payload: { reviewId } }) =&gt; ({
      ...state,
      reviews: state.reviews.filter((item) =&gt; item.id !== reviewId),
    }),
</code></pre>
<p>댓글의 등록과 수정,삭제는 약간 다른 로직을 갖고있는데, 일반적인 등록은 reviewId값이 없는경우 댓글 등록API를 실행하였고, 이후에 리뷰값을 전체 다 받아오는 dispatch(readReview()) 를 실행하여서 댓글을 새로 다 받아 재렌더링 하였다. 이렇게 한 이유는 사용자가 댓글을 쓰는 순간에도 다른 사용자가 댓글을 작성할수있어 받아올때의 댓글List와 작성후의 댓글 List가 다를수있을거라 생각하였기 때문이다.</p>
<p>update와 remove의 경우는 댓글List가 변할수는 있어도, 그 순서에는 변화가 없을것이라 생각하여 API 전송 성공이후, 리덕스에서 보관하는 리뷰의 값만 수정 또는 제거하였다.</p>
<h3 id="평균-별점-및-좋아요-기능-구현">평균 별점 및 좋아요 기능 구현</h3>
<pre><code class="language-javascript">//좋아요를 체크해줫는지 판별하는 state값
const [isSubscribe, setIsSubscribe] = useState(false);
//클릭 이벤트
const onLikeClick = async () =&gt; {
  const response = await subscribePlace({ placeId });
  dispatch(updateLikeCount({ likeCount: response.data }));
  setIsSubscribe(!isSubscribe);
};
//초기 상세페이지 클릭시 받아오는값
  useEffect(() =&gt; {
    async function getData() {
      dispatch(getPlace({ placeId }));
      if (user) {
        const response = await checkSubscribe({ placeId });
        const checked = response.data.isLove;
        setIsSubscribe(checked);
      }
    }
    getData();
  }, [dispatch, placeId, user]);

//리뷰 평점
  const avgRating = () =&gt; {
    if (reviews.length === 0) return 0;
    return (
      reviews.reduce((acc, cur) =&gt; {
        return (acc += parseFloat(cur.reviewRating));
      }, 0) / reviews.length
    );
  };</code></pre>
<p>좋아요의 경우는 처음에 상세페이지의 정보를 받아올때 유저가 로그인 상태라면 서버에 해당 유저가 이 페이지의 좋아요를 누른적이 있는지 bool값으로 받아서 isSubscribe에 저장한다. 유저가 로그아웃상태라면 기본값인 false가 들어간다. 좋아요 버튼을 누를시에는 isSubscribe의 값을 반대로 하도록 하였다. 또한 API에서 받아온 업데이트 이후 좋아요COUNT값을 리덕스에서 변경하였다.</p>
<p>isSubscribe값은 하트아이콘을 표현할때 사용하는데, true면 fill, false면 속이 비어있도록  하였다.</p>
<p>평균 평점은 백엔드쪽에서 따로 받아오는건 없고, 전체 review를 받아오는데, 해당 리뷰의 length와 각 리뷰의 reviewRating을 리듀스를 사용하여 구현하였다.</p>
<h3 id="로딩창-구현">로딩창 구현</h3>
<p>로딩창은 노력대비 자주 보이고, 커서 만족도가 높았다.
첫 구상은 전체 화면의 배경색을 주어 가운데에 loading아이콘이 돌아가는걸 구상하였는데, 현재 라우터에 헤더가 기본 라우터로 설정되어있어서, 헤더부분만 배경색이 적용되지 않았다. 아마 포탈을 사용하여 리액트의 root바깥쪽에서 렌더링하면 될거 같긴한데, 타협하여 무색의 배경화면에 공통 헤더부분을 제외한 화면 가운데에서 스핀만 돌아가게하는걸로 하였다.</p>
<pre><code class="language-javascript">const LoadingPageBlock = styled.div`
  width: 50%;
  height: 50%;
  left: 25%;
  top: 25%;
  position: fixed;
  opacity: 0.3;
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 10;
`;

const LoadingPage = () =&gt; {
  return (
    &lt;LoadingPageBlock&gt;
      &lt;LoadingOutlined style={{ fontSize: &#39;150px&#39; }} /&gt;;
    &lt;/LoadingPageBlock&gt;
  );
};
....
// 사용
  if (loading) return &lt;LoadingPage /&gt;;</code></pre>
<h3 id="마이페이지">마이페이지</h3>
<p>구상한 마이페이지의 옵션은 내가 좋아요 버튼을 누른 장소들의 카테고리와 지역버튼이 모여있는 영역이 따로있어서 선택이 가능하고, 왼쪽에는 아이템 리스트가 나오는 형식이였다.</p>
<p>생각한걸 구현 하기 위해선
페이지에 진입시 GET으로 DB에 data요청 - redux의 places에 정보저장
places에서 카테고리와 지역정보의 중복되지 않은 정보값 필요.
컴포넌트에서 places를 조건 별로 필터링할때 쓰일 state
카테고리와 지역버튼을 담을 state
이 필요하였다.</p>
<p>일단 처음으로 구현한건 카테고리,지역버튼(이하 태그)를 담을 state를 구현한거였는데, </p>
<pre><code class="language-javascript">// 받아온 places에서 중복되지 않는 카테고리와 지역정보값, 이 값들을 사용해서 태그를 렌더링한다.
  const categorys = places
    ? [...new Set(places.map((item) =&gt; item.category))]
    : [];
  const regions = places
    ? [...new Set(places.map((item) =&gt; item.region_1))]
    : [];

  // 선택된 태그를 담을 state
  const [selectedTag, setSelectedTag] = useState({
    categorys: [],
    regions: [],
  });

//태그 클릭 이벤트
  const onClick = (item) =&gt; {
    const key = Object.keys(item).toString();
    let updateItem = [];
    const originalItem = selectedTag[key];
    // 전체 버튼 클릭시
    if (!item[key]) {
      setSelectedTag({
        ...selectedTag,
        [key]: updateItem,
      });
    } else {
      // 태그 클릭시 기존 아이템이 존재하면 제거, 없으면 추가
      updateItem = originalItem.includes(item[key])
        ? originalItem.filter((originItem) =&gt; originItem !== item[key])
        : [...originalItem, item[key]];
      setSelectedTag({
        ...selectedTag,
        [key]: updateItem,
      });
    }
  };

//선택된 태그들의 렌더링, 선택시에는 활성화, 선택되지 않았을시에는 비활성화
  const categorysRender = () =&gt; {
    if (!categorys) return;
    const categorysItem = [];
    categorysItem.push(
      //전체 태그버튼은 처음 한번 생성
      &lt;CustomButton
        shape={&#39;round&#39;}
        key=&quot;cat_all&quot;
        //categorys 태그가 선택된게 없으면 체크true
        checked={!selectedTag.categorys.length}
        onClick={() =&gt; onClick({ categorys: &#39;&#39; })}
      &gt;
        전체
      &lt;/CustomButton&gt;,
    );
    categorys.forEach((item, idx) =&gt; {
      categorysItem.push(
        &lt;CustomButton
          shape={&#39;round&#39;}
          key={idx}
          //해당 태그가 selectedTag.categorys에 존재하면 체크 true
          checked={selectedTag.categorys.includes(item)}
          onClick={() =&gt; onClick({ categorys: item })}
        &gt;
          {item}
        &lt;/CustomButton&gt;,
      );
    });
</code></pre>
<p>이런식으로 구현하였다. 이전에 구현한 모달태그와 크게 다르진 않았다.</p>
<p>이후 선택된 정보태그값을 가지고 useEffect에서 태그값에 의존성을 두었고, 선택된 태그값이 places의 각각의 item들에 포함되어있는 값들만 sortedPlaces라는 places의 정보를 담을 state에 보관하였다.</p>
<pre><code class="language-javascript">  //places 정보를 담고 조건 selectedTag에 따라 필터링할 state
  const [sortedPlaces, setSortedPlaces] = useState([]);

  useEffect(() =&gt; {
    if (
      //선택된 태그가 없으면 전체 정보 저장
      selectedTag.categorys.length === 0 &amp;&amp;
      selectedTag.regions.length === 0
    ) {
      setSortedPlaces(places);
      // 선택된 categorys 태그가 없으면 places의 아이템중에서
      //regions에 저장된 값과 일치하 정보를 필터링
    } else if (selectedTag.categorys.length === 0) {
      setSortedPlaces(
        places.filter((item) =&gt; selectedTag.regions.includes(item.region_1)),
      );
    } else if (selectedTag.regions.length === 0) {
      setSortedPlaces(
        places.filter((item) =&gt; selectedTag.categorys.includes(item.category)),
      );
    } else {
      //태그가 모두 선택 상황일시 두값 다 필터링
      setSortedPlaces(
        places.filter(
          (item) =&gt;
            selectedTag.categorys.includes(item.category) &amp;&amp;
            selectedTag.regions.includes(item.region_1),
        ),
      );
    }
  }, [selectedTag.categorys, selectedTag.regions, places]);</code></pre>
<p>추가적으로 렌더링시에는 필요한 부분만 렌더링 한게아닌, 전체를 다 렌더링하고, 필요하지 않은 정보는 display:none으로 처리 하지 않는 방식을 하였는데, 이 방식을 사용한다면 첫 화면에서 페이지 뒤편까지 모두 렌더링 된 상태이기때문에, 다음 페이지로 넘어갈때 부드럽게 넘어갈수있게된다.</p>
<pre><code class="language-javascript">  const render = () =&gt; {
    const result = [];
    sortedPlaces.map((place, idx) =&gt; {
      //인덱스가 해당 페이지에 맞을때만 보이는 컴포넌트를 생성
      if ((pageInex - 1) * VIEW_ITEM &lt;= idx &amp;&amp; idx &lt; pageInex * VIEW_ITEM)
        return result.push(
          &lt;PlaceItem place={place} key={idx} onLikeClick={onLikeClick} /&gt;,
        );
      //인덱스가 해당 페이지에 해당 되지 않을때 숨김
      else
        return result.push(
          &lt;PlaceItem
            place={place}
            key={idx}
            hidden
            onLikeClick={onLikeClick}
          /&gt;,
        );
    });
   return result;</code></pre>
<p>(마이페이지의 아이템들은 페이지네이션을 하였다. 페이지 인덱스는 1부터 시작이고, 한 페이지에 보여줄 VIEW_ITEM은 4로 설정하였다.)</p>
<p>마지막으로 태그를 클릭하여 sortedPlaces의 값이 바뀌면 pageIndex는 1로 초기화 하도록 하였다. 이부분을 설정하지 않는다면 버그가 발생하게된다.(5페이지를 보고있다 다른 태그를 선택하였는데, 페이지가 그대로 5페이지인채로 있게된다)</p>
<pre><code class="language-javascript">  useEffect(() =&gt; {
    setPageInex(1);
  }, [sortedPlaces]);</code></pre>
<p><img src="https://velog.velcdn.com/images/jun_53/post/aa536590-05f2-422f-9a4e-b8d2f6c17d0a/image.gif" alt="">
(완성 화면)</p>
<p>이번 프로젝트에서 무한 슬라이딩이나 무한스크롤, 그리고 모달 등등을 하였지만
마이페이지의 구현이 가장 React의 장점을 많이 사용할 수 있었던 부분이였던것 같다.
개인적으로 이번 프로젝트에서 가장 마음에 드는 기능구현이였다.</p>
<h3 id="마무리">마무리</h3>
<p>시작할때는 포부넓게 시작하였지만, 하다보니 부족함을 많이 느꼈다.</p>
<p>목표로 했던 타입스크립트 프로젝트는 3월달 이후 실행도 못해봤고,
해보고 싶었던 테스팅코드도 못해봤고,
최적화도 아직 손도 대보지 못했고,
코드 리팩토링도 못하였고,
GIT의 사용도 미숙해서 commit도 일관되게 사용하지 못했다.
심지어 아직 기능구현도 마무리 짓지 못했다.(마감전에는 가능할것 같다..)</p>
<p>그래도 얻은것도 많은 프로젝트라고 생각한다.
가장 중요한, &quot;스스로 프로젝트를 설계에서부터 마무리까지 하였다.&quot; 라는 경험이 가장 큰 결실물이라 생각한다. 돌이켜보면 스스로 공부할때는 숙제 형식이든, 책의 예제를 따라하든 css는 기본적으로 제공되고, 기능구현 위주의 공부였는데, 이번 프로젝트처럼 백지에서 별 다른 도움 없이 시작해서 마무리까지 짓는건 처음이였다. 또한 프로젝트를 진행하면서 필요한 기능은 직접 찾아가면서 공부 및 구현하였고, css도 이전까지는 이해는 하되, 스스로 짤 생각은 못하였는데, 이번 프로젝트에서 실제로 작성하면서 많이 익숙해진것 같다.</p>
<p>백엔드분과의 협업 프로젝트도 이번이 처음이였다.
이전에는 빈약한 지식이긴 했지만, DB도 직접 구현하면서 필요하거나 수정될게 있으면 그때그때 수정하였는데, 이번 프로젝트에서는 백엔드와 프론트의 인원이 따로 나뉘어 있어서 뭔가 이전에 기획했던것 이외에 다른 걸 요구한다던가, 출력 값의 수정을 요구한다던가 하는 갑작스런 요구변경에 미안함을 느꼈다. 
그리고 그런 요구들은 바로 반영되는것도 아니여서, 진행이 정체되기도 하였다.
이번 프로젝트 기능구현 설계의 빈약함과 설계의 중요함을 느끼게 되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[카우치코딩] 5주차 진행 회고]]></title>
            <link>https://velog.io/@jun_53/%EC%B9%B4%EC%9A%B0%EC%B9%98%EC%BD%94%EB%94%A95%EC%A3%BC%EC%B0%A8-%EC%A7%84%ED%96%89-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@jun_53/%EC%B9%B4%EC%9A%B0%EC%B9%98%EC%BD%94%EB%94%A95%EC%A3%BC%EC%B0%A8-%EC%A7%84%ED%96%89-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 25 Apr 2022 09:18:26 GMT</pubDate>
            <description><![CDATA[<h2 id="5주차-진행">5주차 진행</h2>
<ul>
<li>검색 리스트를 받아오고, 무한스크롤을 통해 구현하였다.</li>
<li>상세 페이지 레이아웃 및 데이터처리.</li>
<li>카카오맵을 사용한 위치 지도를 구현하였다.</li>
<li>프록시를 사용하여 실제 DB와의 데이터를 연동하였다.</li>
<li>firebase의 oAuth를 수정하였다.</li>
</ul>
<h3 id="검식-리스트-및-무한스크롤-구현">검식 리스트 및 무한스크롤 구현</h3>
<p>레이아웃 렌더부분 -</p>
<p>각각의 아이템에 사용된 레이아웃은 antd의 card를 사용하였다. 사용된 card는 약간의 커스텀을 하였는데, 기본 제공된 모델에서는 title의 txt가 길경우 자체적으로 overflowhidden의 css가 적용되야하는데 약간의 커스텀을 거치니 정상적으로 작동이 되지 않았다. 
아마 title의 바로 옆에 인라인 요소를 추가로 사용한게 문제였던것 같다. 때문에 antd의 title부분의 width를 강제적으로 90%로 조절하였고 해결하였다.</p>
<p>무한스크롤 부분 - </p>
<p>리액트의 무한스크롤의 첫번째 방법으로 스크롤의 높이와 top,그리고 사용자의 화면의 높이를 실시간으로 비교하여, 일정 조건에서 실행되게 하는 이벤트를 addEventListener 를 통하여 등록하는 방법이 있었다.
하지만 이 방법은 evenet가 스크롤이 움직일때마다 계속 실행되기 때문에 좋은 방법은 아니라고 생각하였다.</p>
<p>두번째 방법은 js에서 제공하는 IntersectionObserver를 사용하는것인데, 이것은 정해둔 타겟의 정해둔 부분만큼 클라이언트의 뷰포인트와 교차하면 정해둔 함수가 실행되는 식이다. 실 구현은 IntersectionObserver를 사용하여 구현하였다.</p>
<pre><code class="language-javascript">
  let index = useRef(false).current;
  let throttle = useRef(false).current;

  const setIndex = useCallback(() =&gt; {
    index++;
  }, [index]);

  const setThrottle = useCallback(() =&gt; {
    throttle = !throttle;
  }, []);

  useEffect(() =&gt; {
       if (throttle) return;
     // IntersectionObserver이 실행될때의 옵션을 지정한다. threshold는 타겟이 얼마만큼 노출되는지에 따라서 실행될것인지를 지정한다.
    const options = {
      threshold: 0,
    };
    // 이벤트가 실행될때 작동하는 함수, entries는 타겟을 값으로 갖고잇으며, 여러가지 기능을 제공한다. 
    const handleIntersection = (entries, observer) =&gt; {
      entries.forEach((entry) =&gt; {
             //옵션에서 지정한 만큼 타겟과 뷰포인트가 교차 하지 않을때는 실행되지 않는다.
        if (!entry.isIntersecting) {
          return;
        }
            // 교차지점에 도달하면 계속 실행이 되기때문에, 쓰로틀을 사용하였다.
        if (index &lt; totalPages) {
          setThrottle();
          setTimeout(() =&gt; {
            onSearch(index);
            setIndex();
            setThrottle();
          }, 1000);
        }
      });
    };    

//위에서 만들 콜백함수와 옵션을 가지고 IntersectionObserver를 생성하고
    const io = new IntersectionObserver(handleIntersection, options);

    if (target.current) {
      //실행한다
      io.observe(target.current);
    }
    return () =&gt; {
      //언마운트시 IntersectionObserver를 제거한다
      io &amp;&amp; io.disconnect();
    };
    };
  }, [index, totalPages, onSearch, throttle, setThrottle, setIndex]);

  &lt;Spacer ref={target} /&gt;

</code></pre>
<p>useEffect 내부에서 리액트의 state를 사용하는경우 의존성을 갖게되며, 값이 변동될때 useEffect가 다시 실행되게 되어, 한번만 실행되어야 하는데 두번 실행되게 된다. 때문에 
Index와 Throttle은 리액트의 state를 사용하지 않고, useRef를 사용하여 구현하였다.</p>
<pre><code class="language-javascript">  useEffect(() =&gt; {
    try {
      if (places.length !== 0) {
        setData((data) =&gt; [...data, ...places]);
      }
    } catch (e) {
      setData([]);
    }
    return () =&gt; {
      if (places.length === 0) setData([]);
    };
  }, [places]);</code></pre>
<p>받아온 데이터는 컴포넌트내의 data State를 만들어서 데이터의 길이가 0이 아닐경우 스프레드 연산자를 사용하여 그대로 붙여서 렌더링 되게 하였다.</p>
<h3 id="상세-페이지카카오맵-구현">상세 페이지,카카오맵 구현</h3>
<p>프로젝트를 진행하면서 가장 스트레스를 받았던건 css와 디자인 구성이였던거 같은데, 그렇다고 딱히 적을만한건 없는것 같다.</p>
<p>이미지와 타이틀 부분은 직접 구현하였고, 정보를 나타내는 Tabs부분은 antd의 tabs를 사용하였다.</p>
<p><img src="https://velog.velcdn.com/images/jun_53/post/99aca195-0be3-4c2f-9659-0707017b8a59/image.JPG" alt="">
(상세 페이지의 정보를 나타내는 탭)</p>
<p>탭의 첫번째 메뉴는 어떤 아이템이라도 필수적으로 존재하는 주소 정보와, 그걸 토대로 카카오맵 API를 사용하였고, 두번째 메뉴는 아이템마다 있을수도 없을수도있는 부가적인 정보가 들어가게된다. 때문에 부가정보가 아예 없을경우에는 나타나지 않게 하였다.</p>
<pre><code class="language-javascript">  const hasContent = (place)=&gt; {
    const content = [&#39;contact&#39;, &#39;cost&#39;, &#39;info&#39;, &#39;link_url&#39;];
    let check = false;
    content.forEach((item) =&gt; {
      if (!check &amp;&amp; place[item] !== null) {
        check = true;
      }
    });
    return check;
  }
    {hasContent(place) ? ....</code></pre>
<p>(content에 해당하는 값이 받아온 데이터에 없을경우 탭 메뉴가 보이지 않도록 설정하였다.)</p>
<p>정보의 텍스트량은 아이템마다 다른데, 너무 길거나 줄바꿈이 2번이상 있을경우는 축약되어 보이게 하였다.</p>
<pre><code class="language-javascript">  const [preViewChecked, setPreViewChecked] = useState({});

  const onPreviewClick = (cat) =&gt; {
    setPreViewChecked({
      ...preViewChecked,
      [cat]: !preViewChecked[cat],
    });
  };

  const preview = (text, cat) =&gt; {
    const result = [];
    let preText = text;
    if (text.length &gt; 70 || text.split(&#39;\n&#39;).length &gt; 2) {
      if (!preViewChecked[cat]) {
        preText = text.replaceAll(&#39;\n&#39;, &#39; &#39;).substr(0, 55).concat(&#39;... &#39;);
      }
      result.push(preText);
      result.push(
        &lt;span
          className=&quot;textToggl&quot;
          onClick={() =&gt; onPreviewClick(cat)}
          key={cat + &#39;_toggle&#39;}
        &gt;
          {!preViewChecked[cat] ? &lt;CaretDownOutlined /&gt; : &lt;CaretUpOutlined /&gt;}
        &lt;/span&gt;,
      );
    } else {
      result.push(preText);
    }
    return result;
  };
....
&lt;pre className=&quot;info&quot; key={&#39;cost_ifno&#39;}&gt;
  {preview(place.cost, &#39;cost&#39;)}
    &lt;/pre&gt;</code></pre>
<p>(조건에 해당되는 텍스트일경우, 축약될 때 ...을 붙이게 하였고, 이벤트를 등록한 아이콘을 옆에 붙여주었다.)</p>
<pre><code class="language-javascript">//카카오맵 api를 사용하기 위해선 index.html에 cdn을 등록해주어야 한다
&lt;script type=&quot;text/javascript&quot; src=&quot;//dapi.kakao.com/v2/maps/sdk.js?appkey=...&quot;&gt;&lt;/script&gt;

//카카오맵에서 제공해주는 API 
const { kakao } = window;

export const KakaoMapScript = (address) =&gt; {
  const container = document.getElementById(&#39;kakaoMap&#39;);
  const options = {
    center: new kakao.maps.LatLng(33.450701, 126.570667),
    level: 3,
  };

  const map = new kakao.maps.Map(container, options);

  const geocoder = new kakao.maps.services.Geocoder();
  geocoder.addressSearch(address, function (result, status) {
    if (status === kakao.maps.services.Status.OK) {
      const coords = new kakao.maps.LatLng(result[0].y, result[0].x);
      const marker = new kakao.maps.Marker({
        map: map,
        position: coords,
      });
      map.setCenter(coords);
    }
  });

  container.style.width = &#39;300px&#39;;
  container.style.height = &#39;300px&#39;;
  map.relayout();
};
// 필요한 컴포넌트에서 useEffect를 사용하여 불러온다.
  useEffect(() =&gt; {
    KakaoMapScript(place.address);
  }, [place.address]);
//   const container = document.getElementById(&#39;kakaoMap&#39;); 에서 사용할 div를 만들어줘야 한다.
&lt;div id=&quot;kakaoMap&quot; /&gt;</code></pre>
<p>카카오맵의 경우는 antd의 tabs와 사용할때 약간의 버그가 있는것 같은데, 처음에 display:none으로 되어 있을때는 제대로 값을 못 받아오는것 같았다. 이미지가 깨져보이고 정상적으로 작동하지 않았다. 원래 계획은 카카오맵을 두번째 탭에서 보여주게 하려는거였는데.. 끝내 해결하지 그냥 디폴트 정보로 나타내는걸로 해결하였다.</p>
<h3 id="연동과-firebase-oauth">연동과 fireBase oAuth</h3>
<p>우리팀은 DB팀에서 헤로쿠를 사용하여 서비스 하였는데, 처음에는 DB에 저장되있는걸 어떻게 사용하는건지 몰라 헤맸다.
결과적으로는 package.json의 proxy 설정을 헤로쿠와 동일하게 해서 해결하였다.</p>
<p>로그인부터 연동을 시작하엿는데, 이전에 구현한 oAuth가 제대로 작동하지 않았다. 백과 프론트 모두 fireBase를 사용하고 잇는데. 결과적으로는 몇몇 문제를 못 찾아서 그런거였다.</p>
<p>첫째로는 백과 프론트 모두 같은 fireBase프로젝트를 사용하여야 하는데, 따로 사용하였기 때문이고. 둘째로는 백에서는 fireBase의 idToken값을 받아서 검증을 하는데, 내가 작성한 프론트에서는 GoogleAuthProvider의 idToken값을 전달하였기 때문이다.
fireBase의 경우 서비스 하는 다른 업체들의 provider값을 취급하기도 하지만, 자체적으로 이것들을 모두 감싼 fireBase의 idToken값이 있다는것을 몰랐기 때문에 일어난 헤프닝이였다.</p>
<p>때문에 이전 oAuth코드를 아래와 같이 변경하였다.</p>
<pre><code class="language-javascript">export const signInGoogle = () =&gt; {
  const provider = new GoogleAuthProvider();
  return signInWithPopup(auth, provider)
    .then((result) =&gt; {
      return result.user.getIdToken();
    })
    .catch((error) =&gt; {
      console.log(error.message);
      throw new Error(error.code);
    });
};</code></pre>
<p>(받아온 result의 user에서 fireBase의 id토큰으로 접근이 가능하다)</p>
<h3 id="마무리">마무리</h3>
<p>무한스크롤 부분은 어떻게 해야 할지 처음엔 감을 잘 못잡았는데, 검색해서 나온것들이 생각보다 실행자체는 잘 되서 좋았다.. IntersectionObserver의 경우는 많은 옵션이 있는것 같은데, 기회가 되면 좀 더 자세히 알아보는게 좋을것 같다. 지금은 너무 실행 위주의 공부였던것 같다.
useEffect에서 IntersectionObserver를 실행하였는데, 그 안에서 리액트의 state를 사용하여 에러가 나는것을 어떻게 해결해야하는지 생각을 못하였다. 이부분은 멘토님께 여쭤봤는데, 따로 useRef를 사용하면 된다 하여서 그 말을 듣고 해결하였다. 생각해보면 당연한거 같다. </p>
<p>되돌아보면 문제가 많아 헤매기도 했지만 덕분에 알게된 지식도 많았던것 같다.. 무한 스크롤 두번실행문제, oAuth연동문제, 카카오맵 display:none으로? 렌더링 될시 이후 작동안됨 문제 등.. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[카우치코딩] 4주차 진행 회고]]></title>
            <link>https://velog.io/@jun_53/%EC%B9%B4%EC%9A%B0%EC%B9%98%EC%BD%94%EB%94%A9-4%EC%A3%BC%EC%B0%A8-%EC%A7%84%ED%96%89-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@jun_53/%EC%B9%B4%EC%9A%B0%EC%B9%98%EC%BD%94%EB%94%A9-4%EC%A3%BC%EC%B0%A8-%EC%A7%84%ED%96%89-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sat, 23 Apr 2022 11:48:11 GMT</pubDate>
            <description><![CDATA[<h3 id="4주차-진행">4주차 진행</h3>
<ul>
<li>메인페이지의 Thumnail을 구현하였다.</li>
<li>카테고리 모달, 지역모달 구현하였다.</li>
<li>리액트 포탈을 사용한 모달을 구현하였다.</li>
<li>antd의 css커스텀 및 스크롤을 숨겨주었다.</li>
</ul>
<h4 id="기능-및-구현구상">기능 및 구현구상</h4>
<p><img src="https://velog.velcdn.com/images/jun_53/post/a351b55f-a3fc-4cda-9519-5169ee90b65d/image.JPG" alt="">(검색값 역할을 하는 썸네일 및 모달 버튼)</p>
<p>내가 구상한 프로젝트에서는 별도의 입력창은 존재하지 않는다.
썸네일을 클릭하면 썸네일의 카테고리 값이 state에 보관되고, 지역모달창이 렌더링되며, 지역모달창을 클릭시 마찬가지로 지역값이 state에 보관된다.
지역은 지역1의 대분류와 지역2의 소분류로 나뉘며, 지역1을 클릭시 지역2가 렌더링된다. 지역2의 경우는 값을 배열로 가지며, 재클릭시 배열에서 삭제된다.
쌈네일외의 카테고리를 보기 위해선, 더보기를 클릭하여 썸네일 외의 것이 등록되잇는 카테고리모달을 띄울수있다.</p>
<p>css도 중요하지만, 구현에 있어 상태관리를 어떻게 할지 고민해보았다.
컨테이너에서 useState를 사용할지, 리덕스를 사용할지 생각을 해보았고, 초기에는 컨테이너에서 useState를 사용하였다.</p>
<p>하지만 이후에 카테고리와 지역 state는 다른 컨테이너에서도 필요로 하기때문에 관리가 어려워, 별도로 리덕스로 빼게 되었다.</p>
<h4 id="구현리덕스-사용전">구현(리덕스 사용전)</h4>
<pre><code class="language-javascript">  const onClick = (item) =&gt; {
    const key = Object.keys(item).toString();
    setSearchParam({
      ...searchParam,
      [key]: item[key],
      region_2: [],
    });
  };</code></pre>
<p>(카테고리 모달과 지역 모달 클릭시 사용되는 함수)
item은 ({ region_1: item }) 와 같이 키와 값이 있는 객체형태로 전달된다.
사용되는 컴포넌트에 따라서 키를 다르게 하여 재활용이 가능하게 하였다.</p>
<pre><code class="language-javascript">  const onClickReg2 = (Reg_2) =&gt; {
    if (searchParam.region_2.includes(Reg_2))
      setSearchParam({
        ...searchParam,
        region_2: searchParam.region_2.filter((item) =&gt; item !== Reg_2),
      });
    else
      setSearchParam({
        ...searchParam,
        region_2: [...searchParam.region_2, Reg_2],
      });
  };</code></pre>
<p>(모달의 지역2를 클릭시 사용되는 함수)
값이 이미 존재하는경우 filter를 사용하여 값을 제거하였고, 없는경우 스프레드 연산자를 사용하여 값을 복사하였다.</p>
<p>지역2의 전체 버튼은 기본 적용이며, 클릭시 지역2 state를 비어잇는 배열 []로 초기화 하도록 하였다. 또한 지역2의 배열이 []이면 자동으로 액티브 상태로 변환되도록 하였다.
카테고리 모달의 경우 값을 8개까지만 보여주었고, 카테고리값이 더 있을경우 마지막으로 ...태그 버튼을 생성하여 클릭시 더 노출될수있게 하였다.</p>
<p>modal의 경우 컴포넌트에 있을경우 보이진 않더라도 html 구조상에 렌더링 될 수있다. 때문에 react의 potal이라는 기능을 사용하였는데, 이것을 사용하면 react에서 사용하는 html의 root div의 아래에 렌더링 되는게아닌, 호출이 될때에만 지정된 root바깥쪽의 요소에서 렌더링 되기 때문에 보이지 않는 모달을 렌더링 하지 않기때문에 모달의 렌더링 비용을 줄일수있다.</p>
<pre><code class="language-javascript">//index.html의 root 바깥에 포탈에 사용될 엘리먼트 생성
    &lt;div id=&quot;root&quot;&gt;&lt;/div&gt;
    &lt;div id=&quot;modalPotal&quot;&gt;&lt;/div&gt;

// portal 컴포넌트
const ModalPortal = ({ children }) =&gt; {
  const globalPortal = document.getElementById(&#39;modalPotal&#39;);
  return ReactDOM.createPortal(children, globalPortal);
};
export default ModalPortal;

//실제 사용
&lt;ModalPortal&gt;
  &lt;CatTagModal title={&#39;카테고리를 선택해주세요&#39;}/&gt;
&lt;/ModalPortal&gt;</code></pre>
<p>4주차에는 js코드의 작성보단 css 작성에 더 많은 시간을 소모하였던것 같다. react 구현은 render부분에서 약간의 로직을 제외하면 크게 문제가 됐던 부분은 없었던것 같다.
카테고리 모달과 썸네일은 직접 css를 작성해서 구현하였고, 지역 모달과 버튼은 antd를 사용하였다. </p>
<p>antd를 사용한 css구현에서 antd는 분명 편리하지만 실제 내가 원하는형태로 사용하기에는 조금 번거롭고 힘들다는것을 느끼게 되었다.
antd에서 제공하는건 레이아웃적으로 여러개로 나뉘어져있고, 각 설정이 있으며, 버튼같은경우에는 :focus와 같은, 내가 원하지 않는 기능까지 모두 다 들어가있다. 
예를 들어 모달의 경우에는 modal에서 사용하는 타이틀 설정을 하단에서 다시 사용하고 싶은데, 이런건 지원하지 않기때문에 별도로 같은 설정을 가진 레이아웃을 만들어야 한다. 그런데 타이틀밑의 레이아웃은 별도의 마진,패딩, width,height 값과 같은게 정해진 상태일때가 많다. 때문에 커스텀해야 하는 경우 이것저것 손을 봐야 하는 경우가 많았다.
버튼의 경우에도 자동적으로 focus 상태일때는 액티브가 되는데, 나는 지역2에서 재클릭시에는 액티브가 해제되기를 바랬는데, 내부에 설정된 옵션때문에 별도로 css 재설정을 해주어야했다.</p>
<pre><code class="language-css">  :focus {
    color: rgba(0, 0, 0, 0.85);
    border-color: #d9d9d9;
  }
  ${(props) =&gt;
    props.checked &amp;&amp;
    css`
      color: #40a9ff;
      border-color: #40a9ff;
      background: white;
      :focus {
        color: #40a9ff;
        border-color: #40a9ff;
      }
    `}</code></pre>
<p>(focus부분 재정의, 한번만 클릭하였을때(checked가 true일때)는 focus가 작동해야 하고, 아닐때는 antd의 focus옵션이 발동하지 않아야한다.)</p>
<p>지역모달에서 지역2 버튼이 렌더링 되면 크기가 너무 커질수 있기 때문에, height는 고정으로 하되, 스크롤 기능이 있긴하지만, 스크롤은 안보이는 형태로 구현하고 싶었다.</p>
<pre><code class="language-html">&lt;div class=&quot;1&quot;&gt;
  &lt;div class=&quot;2&quot;&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>이 기능을 구현하기 위해서 위와 구조에서 &quot;1&quot;의 overflow는 hidden으로 하고, &quot;2&quot;의 overflow는 auto로 하여 스크롤 기능을 사용하되, width의 값을 1보다 크게 하여 스크롤이 위치하는곳이 1의 바깥쪽에 위치하게하여 &quot;1&quot;의 hidden값으로 안보이게 설정하였다.</p>
<h4 id="마무리">마무리</h4>
<p>모달의 경우는 디자인을 받지 못했다. 때문에 디자인 구상까지 모두 내몫이 됐는데, 카테고리 모달의 경우는 직접 구현하였지만 심미학적으로 좀 많은 문제가 있다는것을 느꼈다.. 반면에 antd로 구현한 모달은 그나마 괜찮은 모양세였다. 때문에 디자이너의 소중함이라던가, 심미학적으로는 크게 문제가 없는 antd의 소중함을 느꼇다..</p>
<p>그래도 antd는 가능하면 지양하는게 좋은것 같다. 많은 기능을 제공해주지만 동시에 그게 단점이 되어서 나도 모르는 기능이 있게되고, 이게 의존성을 띄게 되어 생각치않은 버그가 나타날수 있다 생각한다. 또한 antd에서 제공하는것엔 이미 추상화가 꽤나 되어있어서 원래라면 꼭 작성해야 하는 함수까지도 내부에서 처리해주기도 한다. 그리고 내부의 레이아웃도 자동으로 해주기때문에 내가 원하는 레이아웃을 구현하려면 번거로울수있는 수정작업도 거쳐야한다. 물론 뛰어난 개발자분들이 만든것이긴 하겠지만, 그래도 가능하면 외부 라이브러리에 의존하지 않는 코드를 짜고 싶다.</p>
]]></description>
        </item>
    </channel>
</rss>