<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jun_n3.log</title>
        <link>https://velog.io/</link>
        <description>사회에 도움이 되는 것은 꿈, 바로 옆의 도움이 되는 것은 평생 목표인 개발자.</description>
        <lastBuildDate>Tue, 30 May 2023 02:33:14 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jun_n3.log</title>
            <url>https://images.velog.io/images/jun_n3/profile/4b63f886-9272-43af-921a-052186fb6689/귀여운.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jun_n3.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jun_n3" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[순열과 조합]]></title>
            <link>https://velog.io/@jun_n3/%EC%88%9C%EC%97%B4%EA%B3%BC-%EC%A1%B0%ED%95%A9</link>
            <guid>https://velog.io/@jun_n3/%EC%88%9C%EC%97%B4%EA%B3%BC-%EC%A1%B0%ED%95%A9</guid>
            <pubDate>Tue, 30 May 2023 02:33:14 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>알고리즘 공부를 하면서 자주 등장하는 개념인 순열과 조합에 대해서 정리해보기.</p>
</blockquote>
<h1 id="순열permutation">순열(Permutation)</h1>
<h3 id="개념">개념</h3>
<p>순열이란, 서로 다른 n개의 원소에서 r개를 <strong>중복없이 순서에 상관 있게</strong> 선택 또는 나열하는 것을 말한다. 즉 예를 들어 123과 213을 다르게 취급한다는 특징이 있다.</p>
<h3 id="구현">구현</h3>
<pre><code class="language-javascript">const 순열구하기 = (array, r) =&gt; {
  const visit = Array.from({length: array.length}, () =&gt; 0);
  const temp = Array.from({length:r}, () =&gt; 0);
  const result = [];

  const DFS = (level) =&gt; {
      if(r === level){ // 레벨과 뽑을 수가 같아지면 결과값에 푸쉬(123, 132 등)
        result.push(temp.slice());
    } else{
        for(let i = 0; i&lt;array.length; i++){
            if(visit[i] === 0){
                visit[i] = 1; // 체크를 하고
                temp[level] = array[i]; // 값을 넣어주고
                  DFS(level+1); // 다음 레벨로 넘기고
                   visit[i] = 0; // 끝났으니 체크를 초기화 해주기
            }
        }
    }

  }
  DFS(0)
  return result
}</code></pre>
<h1 id="조합combination">조합(Combination)</h1>
<h3 id="개념-1">개념</h3>
<p>조합은 서로 다른 n개의 원소에서 r개를 선택하는 것은 순열과 비슷하지만 순서가 상관 없다는 점이 차이가 있다. 즉, 123과 213을 동일하게 취급한다. </p>
<h3 id="구현-1">구현</h3>
<pre><code class="language-javascript">const 조합구하기 = (array, r) =&gt; {
  const temp = Array.from({length:r}, () =&gt; 0);
  const result = [];

  const DFS = (level, start) =&gt; {
      if(r === level){ // 레벨과 뽑을 수가 같아지면 결과값에 푸쉬(12,13 등)
        result.push(temp.slice());
    } else{
        for(let i = start; i &lt;= array.length; i++){
          temp[level] = i; // 값을 넣어주고
          DFS(level+1, i+1); // 다음 레벨로 넘기고  
        }
    }

  }
  DFS(0,1)
  return result
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[React | React-hook-form 가볍게 살펴보기]]></title>
            <link>https://velog.io/@jun_n3/React-React-hook-form-%EA%B0%80%EB%B3%8D%EA%B2%8C-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@jun_n3/React-React-hook-form-%EA%B0%80%EB%B3%8D%EA%B2%8C-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Sun, 07 Aug 2022 08:23:04 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>웹 어플리케이션을 만들다 보면 수 많은 <strong>Form</strong> 들을 만나게 된다. 상황에 따라서는 단순히 2~3개의 입력이 아니라 여러 개의 입력을 받고 유효성 검증까지 함께 해야하기 때문에 매번 골머리를 않는 부분이기도 하다. 특히, 사용자의 입력마다 유효성을 검증하고 에러 메세지를 보여줘야 하는 경우에는 렌더링 최적화에 대한 고민이 들어가게 되고, 이를 이해 코드를 작성하다 보면 또 엄청 복잡한 코드를 마주하고는 인상을 쓰게 된다. 팀 간에 협업을 진행하면서도 <strong>Form</strong>을 어떻게 작성할 지 협의가 쉽지 않아지는 포인트이기도 하다. 이를 개선하기 위해서 도움을 받을 수 있는 라이브러리가 있는 지 찾아보게 되었고 나는 <strong>React-hook-form</strong> 은 어떤 라이브러리인지, 어떤 문제를 도와줄 수 있는지 살펴보려고 한다. </p>
<h2 id="설치하기">설치하기</h2>
<pre><code># 지원하는 리액트 버전
npm i react-hook-form

# 지원하지 않는 리액트 버전
npm i react-hook-form --legacy-peer-deps</code></pre><h2 id="기존-form-방식">기존 Form 방식</h2>
<pre><code>const App = () =&gt; {
  const [values, setValues] = useState({
    email: &quot;&quot;,
    password:&quot;&quot;,
    passwordCheck:&quot;&quot;,
    phone:&quot;&quot;,
    agreement: &quot;&quot;,
  })

  const onChange = (e:React.ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {
    const {name, value} = e.target;

    setValues(prev =&gt; ({
      ...prev, [name] : value.trim()
    }))
  }


  return( 
    &lt;Container&gt;
    &lt;Input name=&quot;email&quot; value={values.email} onChange={onChange}/&gt; 
        &lt;Input name=&quot;password&quot; value={values.password} onChange={onChange}/&gt;
        &lt;Input name=&quot;passwordCheck&quot; value={values.passwordCheck} onChange={onChange}/&gt;
        &lt;Input name=&quot;phone&quot; value={values.phone} onChange={onChange}/&gt;
        &lt;Input name=&quot;agreement&quot; value={values.agreement} onChange={onChange} type=&quot;checkBox&quot;/&gt;
    &lt;/Container&gt;
   )
}</code></pre><p>기존에는 폼을 작성하기 위해서 신경 쓸 부분이 많았다. 각 input에 대한 상태값을 관리할 필요가 있었고 이를 위해 각 상태를 따로 작성하거나 하나의 useState에 객체로 관리하거나 해야했다. 여기에 각 입력마다 error 상태를 관리하기 위해서는 error에 관한 상태를 또 만들고 매 입력마다 이를 확인하는 로직을 추가해야 했다. <strong>React-hook-from</strong>을 사용하면 이를 훨씬 간단하게 작성할 수 있다.</p>
<h2 id="react-hook-form-적용하기">React-hook-Form 적용하기</h2>
<h3 id="useform">useForm</h3>
<p>useForm을 이용하면 번거롭고 복잡한 코드를 간단하게 작성할 수 있다. </p>
<pre><code>import React from &quot;react&quot;;
import { useForm } from &quot;react-hook-form&quot;;

export default function App() {
  const { register, handleSubmit } = useForm();
  const onSubmit = data =&gt; console.log(data);

  return (
    &lt;form onSubmit={handleSubmit(onSubmit)}&gt;
      &lt;input {...register(&quot;firstName&quot;)} /&gt;
      &lt;select {...register(&quot;gender&quot;)}&gt;
        &lt;option value=&quot;female&quot;&gt;female&lt;/option&gt;
        &lt;option value=&quot;male&quot;&gt;male&lt;/option&gt;
        &lt;option value=&quot;other&quot;&gt;other&lt;/option&gt;
      &lt;/select&gt;
      &lt;input type=&quot;submit&quot; /&gt;
    &lt;/form&gt;
  );
}</code></pre><p>우리가 연결하고자 하는 <strong>Input</strong> 에 <strong>register</strong> 을 이용해서 연결할 수 있는데, 이때 인자로 넣어주는 문자열은 <strong>name</strong>이라고 생각하면 된다.</p>
<p><strong>handleSubmit</strong>의 경우에는 <strong>submit</strong>의 기본 이벤트가 발생할 때 리프레쉬 되는 현상을 막아주면서 콜백으로 전달된 함수에 input에 담긴 value 들을 담아 실행시켜주기 때문에 복잡하게 추가 로직을 작성할 필요 없이 사용할 수 있다. </p>
<h4 id="컴포넌트에-적용하기"><strong>컴포넌트에 적용하기</strong></h4>
<p>위의 register 방식을 통하면 손쉽게 form을 관리할 수 있지만 다른 라이브러리에서 제공하는 컴포넌트나 내부에서 작성한 컴포넌트의 경우에는 바로 register을 적용할 수 없다. 이를 대비해서 <strong>Controller</strong> 가 제공된다.</p>
<pre><code>const {
    control,
    getValues,
    formState: {errors, isValid}

} = useForm({mode: &quot;onChange&quot;, reValidateMode: &quot;onChange&quot;})


return  &lt;InputWrapper&gt;
          &lt;Controller
            render={({ field }) =&gt; (
            &lt;WithLabelInput
              type=&quot;text&quot;
              label=&quot;이름&quot;
              {...field}
            /&gt;
          )}
            control={control}
            name=&quot;이름&quot;
            rules={{ required: true }}
            defaultValue=&quot;&quot;
          /&gt;
          &lt;SpaceY space=&quot;8px&quot; /&gt;
          {errors?.이름 &amp;&amp; (
            &lt;WarnMessage
              message=&quot;이름을 입력해주세요.&quot;
              isError
              fontSize=&quot;14px&quot;
            /&gt;
          )}
        &lt;/InputWrapper&gt;</code></pre><p><strong>Controller</strong>에 각• 요소는 다음과 같다.</p>
<ul>
<li><strong>render :</strong> 전달하고자 하는 컴포넌트를 전달할 수 있다. 에러메세지를 방지하기 위해서 직접 구현한 컴포넌트의 경우에는 ref를 전달받을 수 있는 형태로 작성하고, defaultValue를 통해서 밸류 관련 에러도 방지할 수 있다. </li>
<li><strong>rules :</strong> 유효성 검증을 위해 필요한 내용을 입력해 줄 수 있다. 상황에 따라서 <strong>required, pattern</strong> 등을 사용할 수 있다. 타입과 메세지를 입력하는 경우에는 유효성 검증을 통과하지 못한 경우에 보여줄 메세지도 입력할 수 있다. </li>
<li><strong>errors :</strong> 유효성 검증을 통과하지 못한 경우에는 에러 객체에 내용이 담기게 된다. type과 message의 형태이기 때문에 각 상황에 맞게 에러 메세지를 보여줄 수 있다.</li>
<li><strong>isValid :</strong> 유효성 검증이 마무리 되어 있는지 상태를 boolean으로 알려주는 값이다. 제출 버튼의 활성화 상태와 관련해서 사용할 때 유용할 수 있다. </li>
</ul>
<h2 id="적용-모습">적용 모습</h2>
<p><img src="https://velog.velcdn.com/images/jun_n3/post/6a6fd70a-12be-41ec-a050-cca2654be91e/image.gif" alt=""></p>
<p>간단하게 작성해 본 입력 modal의 모습이다. 각 input의 유효성을 실시간으로 검색하면서도 리렌더링을 유발시키지 않고 있다. 또한, 동일한 동작을 하는 기능임에도 불구하고 훨씬 적은 코드를 작성할 수 있었다.</p>
<h2 id="개선이-예상-되는-점">개선이 예상 되는 점</h2>
<h4 id="기존">기존</h4>
<ul>
<li>여러가지 입력의 상태를 관리하며 에러 메세지를 보여줘야 했기 때문에 많은 리 렌더링이 발생해야 했다. 입력이 적은 경우에는 문제가 없겠지만 많은 경우에는 불필요하게 많은 리렌더링이 성능에 나쁜 영향을 줄 수 있다고 생각했다.</li>
<li>렌더링을 줄이기 위해서는 memo 등을 이용해 각자 최적화를 해야 했고, 이를 위해서 입력을 관리하는 로직이 지나치게 복잡해지는 경향이 있었다. 특히 여러 개의 입력을 관리하는 경우에는 상태를 계속 전개 연산자를 통해 복사하면서 진행해야 해서 실수를 유발하기 쉬웠다.</li>
</ul>
<h4 id="변경">변경</h4>
<ul>
<li>React-hook-form의 경우에는 옵션에 따라서 리 렌더링을 발생시키지 않을 수 있기 때문에 불필요한 렌더링을 줄일 수 있었다.</li>
<li>상태를 직접적으로 관리할 필요 없이 React-hook-form 에서 제공하는 함수를 통해 가져올 수 있었으며, 에러 메세지 또한 에러 객체를 통해서 쉽게 적용할 수 있었다.</li>
</ul>
<h2 id="마무리">마무리</h2>
<p>이렇게 React-hook-form에 대해서 간단하게 살펴 봤다. 당장 프로덕트에 적용한 것이 아니라 검토하는 과정에서 간단하게 작성을 해봤기 때문에 실제로 사용하면서 많은 문제들이 발생할 여지는 있다. 항상 일하면서 느끼는 부분이지만 개발이 들어가게 되는 경우에 생각보다 많은 변수들이 발생하고 이를 해결하기 위해서 라이브러리와 고생을 해본 경험은 많으니까. 그렇기 때문에 직접 작성하고 최적화만 잘하면 되지 않겠냐는 생각이 들 수도 있고, 한 적도 있다. 그럼에도, 라이브러리를 사용해서 얻을 수 있는 이점은 불필요한 코드의 양을 줄이고 협업하는 과정에서 의사 소통에 들어가는 시간의 낭비를 줄일 수 있는 부분이라고 생각한다. 적은 코드를 작성하면 버그도 줄어들테니까. 다음 React-hook-form과 관련된 글은 실제로 적용하면서 겪었던 문제들과 이를 해결하는 과정에 대해서 정리해보려고 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React | 공통 API 에러 처리 제작기(feat. React Query)]]></title>
            <link>https://velog.io/@jun_n3/React-%EA%B3%B5%ED%86%B5-API-%EC%97%90%EB%9F%AC-%EC%B2%98%EB%A6%AC-%EC%A0%9C%EC%9E%91%EA%B8%B0feat.-React-Query</link>
            <guid>https://velog.io/@jun_n3/React-%EA%B3%B5%ED%86%B5-API-%EC%97%90%EB%9F%AC-%EC%B2%98%EB%A6%AC-%EC%A0%9C%EC%9E%91%EA%B8%B0feat.-React-Query</guid>
            <pubDate>Wed, 15 Jun 2022 07:07:15 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>회사 프로젝트에 React Query를 도입시켜보면서 그동안 함부로 건들지 못했던 API 핸들링 관련 로직을 건들어야만 했다. React Query 잘 도입해서 팀원들이 편하게 사용할 수 있으면 좋겠다는 마음이 들어 대대적으로 한번 뜯어봤다. 아래는 기존에 사용하던 API 에러 핸들링을 더 잘해보기 위해서 고민한 내용이다.</p>
<h3 id="고민하게-된-계기">고민하게 된 계기</h3>
<p>React Query만 잘 도입하면 되겠다 싶었는데, 세션에 대한 처리가 필요했다. 기존에는 세션에 관련된 커스텀 훅을 만들고 여기에 API 응답을 넣어서 처리가 될 수 있도록 했는데 React Query를 도입하면서 동일하게 처리할 수 없었다. React Query에서는 이를 onError 옵션을 통해 처리할 수 있도록 하고 있는데 기존 세션 만료 응답은 200으로 오고 있어 에러로 처리할 수 없었다. 이를 select 등의 옵션에서 처리하는 것은 가독성도 헤쳤고 시간이 많이 흐른 뒤에 의도를 파악하기 힘들어 유지보수에도 악영향을 끼칠 것이라는 생각이 들기도 했다. 그래서 여러가지 시도를 통해 문제를 파악하다가 결국 API 핸들러 레벨부터 수정을 해보기로 했다. </p>
<h3 id="목표">목표</h3>
<p>크다면 큰 작업이 될 예정이었기 때문에 간단하게 목표를 통해 제한범위를 정해서 진행해보기로 했다. </p>
<p>1. 기존 API 핸들러를 그대로 두되, 새로운 API 핸들러를 만들고 이용한다.</p>
<p>2. useApiError를 통해 공통 에러 관련 로직을 관리해 유지보수가 편하도록 한다.</p>
<p>3. 필요한 경우 사용하는 컴포넌트에서 추가 로직을 주입할 수 있도록 한다.</p>
<h2 id="api-핸들러-만들기">API 핸들러 만들기</h2>
<p>기존 API 핸들러의 기본 로직은 아래와 같았다. </p>
<pre><code class="language-javascript">export const receive = (promise: AxiosPromise&lt;any&gt;): Promise&lt;ApiResponse&gt; =&gt;
    new Promise((resolve, reject) =&gt; {
        promise
            .then((response: AxiosResponse) =&gt; {
                if (
                    응답 결과 === &quot;A&quot; || 
                    응답 결과 === &quot;B&quot; ||
                    응답 결과 === &quot;C&quot; ||
                    응답 결과 === &quot;D&quot; ||
                    응답 결과 === &quot;E&quot; ||
                    !응답 결과

                ) {
                    const data = response.data;

                    const headers = response.headers;

                    const apiResponse: ApiResponse = {
                        result: ApiResult.OK,
                        data,
                        headers,
                    };

                    resolve(apiResponse);
                } else {
                    // 공통 error handle 처리
                    const apiError: ApiError = {
                        result: ApiResult.UNKNOWN_ERROR,
                        message: &#39;unknown error.&#39;,
                        data: response.data,
                    };

                    // 에러 내용을 주입하는 로직

                    reject(apiError);
                }
            })
            .catch((reason: AxiosError&lt;{ additionalInfo: string }&gt;) =&gt; {
                const apiError: ApiError = {
                    result: String(reason?.response?.status),
                    message: reason.message,
                    data: reason?.response?.data,
                };

                reject(apiError);
            });
    });</code></pre>
<p>기존 API의 경우 모든 응답이 200으로 왔고 따로, 응답 안에서 코드를 보내주는 형식이었다. 그렇기 때문에 200 응답이 온 경우에 코드의 조건을 비교하고 일치하지 않으면 에러를 발생시키도록 되어 있었다. 하지만, API 응답 구조가 변경되면서 수정이 필요했는데 급하게 프로젝트를 진행하기 위해서 <em><strong>!응답결과</strong></em> 라는 조건을 통해서 사용할 수 있도록 했었다. </p>
<p>이렇게 사용하다보니 조건이 추가되는 경우마다 A,B,C,D 늘어나면서 알아보기 어려워지고 사이드 이펙트가 두려워 쉽게 수정도 하기 어려운 상황이 되고 있었다. 아예 뜯어 고칠 수 있었다면 좋았겠지만 이를 위해서는 여러 영역에서 대대적인 수정이 필요하게 되어 우선 새로운 API 핸들러를 만들어 보았다. </p>
<pre><code class="language-javascript">export const axiosRequest = (
    promise: AxiosPromise&lt;any&gt;
): Promise&lt;AxiosResponse&gt; =&gt;
    new Promise((resolve, reject) =&gt; {
        promise
            .then((response: AxiosResponse) =&gt; {
                const data = response?.data;

                if (
                   (!응답결과 || 응답결과 === &quot;200&quot;) &amp;&amp; (세션관련 처리)
                ) {
                    const apiResponse: AxiosResponse = response;

                    resolve(apiResponse);
                } else {
                    // 여기에 공통 error handle 처리
                    const apiError: ApiError = {
                        result: ApiResult.SESSION_OUT,
                        message: &#39;세션 정보가 없습니다.&#39;,
                        data: response.data,
                    };

                    reject(apiError);
                }
            })
            .catch((reason: AxiosError&lt;{ additionalInfo: string }&gt;) =&gt; {
                const apiError: ApiError = {
                    result: String(reason?.response?.status),
                    message: reason.message,
                    data: reason?.response?.data,
                };

                reject(apiError);
            });
    });</code></pre>
<p>보이는 것처럼 눈에 띄는 엄청난 변화는 없지만, 우선 지저분한 여러 조건들을 삭제하였고 세션 관련 처리 로직을 추가하였다. 이제 세션이 없는 경우에 200이 아닌 403 에러를 받을 수 있게 되었다. 욕심을 부려서 대대적으로 수정을 할 수도 있지만 이번 목표가 모든 영역을 아우르는 것이 아니었기 때문에 이후 작업을 진행했다. </p>
<h2 id="에러-흐름-파악하기">에러 흐름 파악하기</h2>
<p>API 핸들러를 새로 만들었기 때문에 이제 이를 이용하면서 진행할 공통 에러 처리 로직을 만들 차례였다. React를 사용하는 프로젝트이기 때문에 어디서든 사용할 수 있도록 커스텀훅을 이용했다. </p>
<p>우선, 회사 프로젝트에서 사용되는 에러의 종류는 이렇다. </p>
<p>1. 기존 API의 경우 모든 응답은 200, 상황에 따라 result를 통해 에러 코드를 보낸다. (10003, 10004 등)</p>
<p>2. 신규 개발되는 API 경우 200, 400 등의 상태로 응답이 오며, 필요한 경우에 errorCode를 보낸다.</p>
<p>3. 필요한 경우, 동일한 errorCode라고 해도 다른 errorMessage를 가지는 경우가 있다.</p>
<p>기본적으로 2번과 3번의 상황에 사용될 수 있도록 아래의 흐름으로 로직을 구성했다. 관련하여 <a href="http://blog.hwahae.co.kr/all/tech/tech-tech/7867/">화해(버드뷰)의 블로그</a>에서 아이디어를 얻을 수 있었다. </p>
<p><img src="https://velog.velcdn.com/images/jun_n3/post/3729fda5-3ad4-4103-a980-9923f59bdf58/image.jpeg" alt=""></p>
<p>그림에서 보이는 것처럼 에러의 처리는 아래 순서로 진행하기로 했다. </p>
<p>1. API 요청에서 에러가 발생하는 경우 먼저, 상태코드를 확인한다.</p>
<p>2. 해당하는 상태코드에 추가적인 에러코드가 없다면 상태코드로 분기하여 관련 로직을 수행한다.</p>
<p>3. 해당하는 상태코드에 추가적으로 에러코드가 있다면 상태코드로와 에러코드를 이용하여 분기한 후 관련 로직을 수행한다.</p>
<h2 id="실제-구현">실제 구현</h2>
<h3 id="useapierror-커스텀-hook">useApiError 커스텀 hook</h3>
<pre><code class="language-javascript">import { useCallback } from &#39;react&#39;;

type HandlersType = {
    [status: number | string]: any;
};

export const useApiError = (handlers?: HandlersType) =&gt; {

    const handle403 = () =&gt; {
        // 세션 만료 팝업 호출
    };

    const handle500 = () =&gt; {
        // 500 상태 관련 로직
    };

    const handleDefault = () =&gt; {
        // 기본 에러 처리 로직
    };

    // 기본적으로 처리될 수 있는 에러 핸들러
    const defaultHandlers: HandlersType = {
        403: {
            default: handle403,
        },
        500: {
            default: handle500,
        },
        default: handleDefault,
    };

    const handleError = useCallback(
        error =&gt; {
            const httpStatus = error.result;
            const errorCode = error.data.errorCode;
            const errorMessage = error.data.errorMessage;

            switch (true) {
                case handlers &amp;&amp;
                    !!handlers[httpStatus]?.[errorCode]?.[errorMessage]:
                    handlers![httpStatus][errorCode][errorMessage]();
                    break;

                case handlers &amp;&amp; !!handlers[httpStatus]?.[errorCode]:
                    handlers![httpStatus][errorCode](error);
                    break;

                case handlers &amp;&amp; !!handlers[httpStatus]:
                    handlers![httpStatus].default(error);
                    break;

                case !!defaultHandlers[httpStatus][errorCode]:
                    defaultHandlers[httpStatus][errorCode]();
                    break;

                case !!defaultHandlers[httpStatus]:
                    defaultHandlers[httpStatus].default();
                    break;

                default:
                    defaultHandlers.default();
            }
        },
        [handlers]
    );

    return { handleError };
};</code></pre>
<p>useApiError 라는 커스텀 hook 안에서 공통으로 처리될 수 있는 로직을 정의했고 이를 switch를 이용해서 우선순위에 따라서 처리될 수 있도록 했다. 우선순위는 아래와 같다. </p>
<p>1. 특정 컴포넌트에서 정의한 핸들러이며, errorMessage에 따라서 분기가 필요한 경우</p>
<p>2. 특정 컴포넌트에서 정의한 핸들러이며, errorCode에 따라서 분기가 필요한 경우</p>
<p>3. 특정 컴포넌트에서 정의한 핸들러이며, 상태 코드에 따라서 분기가 필요한 경우</p>
<p>4. hook에서 정의된 핸들러이며, 상태코드 및 errorCode에 따라서 분기가 필요한 경우</p>
<p>5. hook에서 정의된 핸들러이며, 상태코드에 따라서 분기가 필요한 경우</p>
<h3 id="기본적인-사용법">기본적인 사용법</h3>
<pre><code class="language-javascript">const useCustomQuery = () =&gt; {
    const {handleError} = useApiError();

    return useQuery([queryKey], fetchApi, {
        onError: handleError
    })
}

export default useCustomQuery;</code></pre>
<p>특별한 추가 로직이 없는 경우에는 handleError를 바로 이용할 수 있다. 이를 통해서 API 통신을 하는 로직에 복잡한 에러 로직이 들어갈 필요가 없고, 굳이 노출할 필요가 없는 부분을 추상화 시킬 수 있다. 만약, 특정 동작을 추가하고 아래처럼 주입할 수 있다.</p>
<h3 id="핸들러-주입하기">핸들러 주입하기</h3>
<pre><code class="language-javascript">const useCustomQuery = () =&gt; {
    const {handleError} = useApiError({
         400: {
            [에러코드]: () =&gt;
                openFailedToast({ message: &#39;잘못된 정보입니다.&#39; }),
        },

    });

    return useQuery([queryKey], fetchApi, {
        onError: handleError
    })
}

export default useCustomQuery;</code></pre>
<p>기본적으로 <strong><em>handleError</em></strong> 의 경우에 defaultHandler에 정의 된 로직을 처리하도록 되어있지만 hook을 호출하는 시점에서 핸들러를 주입하면 이를 이용할 수 있다. 따라서, 추상화 레벨은 유지하면서 사용하는 컴포넌트의 상황에 따라서 필요한 로직을 주입할 수 있게 된다. </p>
<h2 id="마치며">마치며</h2>
<p>기존에는 API 관련 함수를 볼 때 여러가지 에러 처리 로직이 뒤섞여 있었고 이 때문에 복잡한 구현 내용을 파악해야만 했다. 특히, 한 API에서 여러가지 에러 코드를 다뤄야 하는 경우에는 복잡하다보니 실수도 자주 발생했다. 때문에 이를 쉽게 만들어 줄 수 있는 방법을 고민하고 있어 찾는 도중 화해(버드뷰)에서 작성해주신 글을 보고 좋은 아이디어를 얻을 수 있었다. 기존의 API에도 모두 적용할 수는 없었지만 앞으로의 개발에서는 이를 사용해서 팀원들이 편하게 사용할 수 있으면 좋겠다. 그리고 앞으로의 유지보수에 기여가 됐으면 한다. </p>
<hr>
<p>참고 : <a href="http://blog.hwahae.co.kr/all/tech/tech-tech/7867/">화해(버드뷰) - React Query와 함께하는 API에러 처리 설계하기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Query 시작하기]]></title>
            <link>https://velog.io/@jun_n3/React-Query-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jun_n3/React-Query-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 02 Jun 2022 01:08:19 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>기업협업을 진행하던 당시에 면접을 보고 온 동기가 useEffect 대신할 수 있는 라이브러리가 있다는 말을 했다. 그 당시에는 무엇인지 제대로 몰랐지만 얼마 후 그 라이브러리가 React Query 라는 것을 알게 됐다. 물론, useEffect를 대신하는 라이브러리라고 보기에는 어렵지만 useEffect를 이용해 데이터를 가져오지 않아도 되는 것은 맞았다. 이후 우아한 테크세미나를 통해 React Query에 대해서 발표를 듣기도 했지만 간단한 테스트 정도만 진행해보고 제대로 도입을 고민해보지는 못했다. 하지만, 데이터를 잘 다루기 위해 Redux를 사용하고, 비동기를 잘 처리하기 위해 여러가지 미들웨어까지 도입하고 있자면 절실히 다른 방법을 찾고 싶은 생각이 들곤 했다. 상태 관리 라이브러리라는 이름과는 다르게 Redux에는 온갖 비동기 관련 로직들이 가득해지곤 했으니까. </p>
<p>아래는 React Query 공식 사이트에서 볼 수 있는 React Query의 소개 문구이다. </p>
<blockquote>
<p>Fetch, cache and update data in your React and React Native applications all without touching any &quot;global state&quot;.</p>
</blockquote>
<p>즉, 데이터를 가져오고, 캐시하고, 동기화하고 업데이트 하는 작업을 <strong>전역 State</strong> 없이 수월하게 할 수 있도록 해주는 라이브러리이다.</p>
<h2 id="도입하게-된-계기">도입하게 된 계기</h2>
<p>우선, React Query의 특징을 정리해보면 이렇다.</p>
<p>1. <strong>Server State</strong>와 <strong>Client State</strong>라는 개념을 분리하여 프론트엔드에서 Server State를 다루는 방법을 재정의하고 있다. </p>
<p>2. Server State를 다루는 방법에 있어 <strong>SWR(Stale-While-Revalidate)</strong> 전략을 통해 해결하고 있다. </p>
<p>3. <strong>useQuery와 같은 Hook 인터페이스</strong>로 제공되기 때문에 도입이 어렵지 않고 기존 방식보다도 간결하게 사용할 수 있다.</p>
<p>React Query가 좋다는 건 알겠는데, 그래서 굳이 기존의 방식을 벗어나 새롭게 도입해야하는 이유는 무엇일까?</p>
<p>우선, 나는 기존에 Server State와 Client State를 혼용하며 관리했다. 특히 Server State의 경우에는 클라이언트가 아닌 서버에서 관리되고 변경되는 상태였기 때문에 언제나 &quot;out of date&quot; 될 수 있는 위험이 있었다. 그렇기 때문에 지속적으로 데이터 업데이트 및 관리, 캐싱 등에 대해서 고민을 할 필요가 있었다. 이를 해결하기 위해 Redux 등을 사용하면 무수한 미들웨어와 많은 보일러 플레이트 코드가 따라오곤 했다. </p>
<p>심지어, 서버의 데이터를 가져오고 업데이트 하는 과정에서 사용자의 UX를 위해 로딩 등의 상태를 처리하기 위해서 추가적으로 Client State 를 늘려야만 했다. 아래처럼.</p>
<pre><code class="language-javascript">const App = () =&gt; {
  const [isLoading, setIsLoading] = useState();


  const fetchData = async () =&gt; {
      setIsLoading(true) // 로딩 상태로 변경하고


    try {
      const data = await apiFunction()

      if(data) {
          setIsLoading(false); // 로딩을 끝낸다.
        // ... 이후 로직
      }
    }
    catch(error) {
        console.log(error);
        setIsLoading(false);
    }
  }
}


export default App;</code></pre>
<p>페이지가 늘어날 수록, 기능이 많아질 수록 Loading 등과 관련 된 상태를 개발자가 직접 구현하고 핸들링 해야하는 부담은 커지게 된다. 이는 휴먼 에러가 생길 가능성이 늘어난다는 말과 같다. 실제로 개발을 진행하며 여러가지 상태를 관리하는 과정에서 로딩 관련 상태를 놓치는 경우가 생겨 무한 로딩을 하게 되는 경우가 발생했다. </p>
<p>이러한 고민들을 React Query는 각종 보일러 플레이트나 러닝 커브 없이 기존에 하던 방식과 유사하고 더욱 간결하게 해결할 수 있도록 해준다. React Query과 어떻게 이를 해결하고 있는지 살펴보자. </p>
<h2 id="사용-방법"> 사용 방법</h2>
<p>간단한 예시를 통해서 리액트 쿼리의 사용 방법을 알아보자.  </p>
<h4 id="설치법">설치법</h4>
<pre><code>npm i react-query // npm 사용

yarn add react-query // yarn 사용</code></pre><h4 id="초기-설정">초기 설정</h4>
<pre><code class="language-javascript">import {QueryClientProvider, QueryClient} from &quot;react-query&quot;;

const queryClient = new QueryClient();

function App() {
  return (
      &lt;QueryClientProvider client={queryClient}&gt;
          {...}
      &lt;/QueryClientProvider&gt;);
}</code></pre>
<p>React Query는 데이터를 전역 상태처럼 사용할 수 있도록 제공하고 있기 때문에 이를 위해 Provider로 전체 앱을 감싸줘야 한다. 이후에는 사용하고자 하는 곳에서 useQuery를 통해 데이터를 받아올 수 있다. </p>
<h4 id="usequery">useQuery</h4>
<p>데이터를 가져오는 경우에는 useQuery hook을 사용하면 된다. useQuery는 아래와 같은 구조를 가지고 있다. </p>
<pre><code class="language-javascript">// 일반적
const responseData = useQuery(쿼리 키, 비동기 함수, 옵션);

// 구조분해 할당
const {data, isLoading, isFetching} = useQuery(쿼리 키, 비동기 함수, 옵션);</code></pre>
<p>쿼리 키의 경우에는 string이나 배열로 전달할 수 있고 배열로 전달하는 경우에는 [&quot;key&quot;, &quot;first&quot;], [&quot;key&quot;, &quot;second&quot;] 와 같이 &quot;key&quot; 가 동일하다고 해도 다른 쿼리로 인식된다. 더 확실한 관리를 위해서 배열의 형태로 전달하는 것을 추천한다. </p>
<p>실제 사용법은 아래와 같다. 실제로 이번 스프린트에 사용되는 코드에 적용된 모습을 수정했다.</p>
<pre><code class="language-javascript">const useListQuery = (options?: any) =&gt; {
    const { openSuccessToast, openErrorToast } = useToast();

    const queryClient = useQueryClient();

    const { data, isLoading } = useQuery([&quot;list&quot;], getList, {
        onError: () =&gt; {
            openErrorToast({});
        },

        onSuccess: () =&gt; {
            openSuccessToast({});
        },

        refetchOnWindowFocus: false,
    });

    const invalidateClientListQuery = () =&gt; {
        queryClient.invalidateQueries(&#39;list&#39;);
    };

    return {
       data,
       isLoading,
    };
};

export default useListQuery;</code></pre>
<p>React Query의 TkDodo는 쿼리를 컴포넌트 옆에서 관리하자고 제안했다. (링크 : <a href="https://tkdodo.eu/blog/effective-react-query-keys#colocate">Effective React Query Keys</a>) 폴더 구조까지 적용하지는 않더라도 커스텀 훅으로 분리하여 쿼리를 관리하는 부분은 컴포넌트와 데이터 통신의 유착을 줄이고 유지보수에도 도움이 될 것이라고 생각한다.  위의 코드에서 알 수 있듯이 isLoading이나 isFetching 등의 상태를 제공하고 있기 때문에 추가로 상태를 만들어 관리할 필요가 없으며 데이터를 위한 상태를 만들 필요도 없다. data를 이용해 바로 사용하면 된다! </p>
<p>만약 서버로 받은 데이터를 가공해야 한다면? options 중에서 select를 사용할 수 있다. </p>
<pre><code class="language-javascript">const useCountryListQuery = (lang: string) =&gt; {
    const queryClient = useQueryClient();

    const { data } = useQuery(
        [&#39;countryList&#39;, lang],
        () =&gt; getCountryList(lang),
        {
            select: res =&gt; {
                return {
                    ...res,
                    data: res.data.body.map(item =&gt; {
                        return {
                            ...item,
                            label: item.name,
                            value: item.name,
                        };
                    }),
                };
            },
            refetchInterval: 60 * 60 * 1000,

            refetchOnWindowFocus: false,
        }
    );

    const invalidateCountryListQuery = () =&gt; {
        queryClient.invalidateQueries(&#39;countryList&#39;);
    };

    return {
        countryList: data?.data,
        invalidateCountryListQuery,
    };
};

export default useCountryListQuery;</code></pre>
<p>위의 코드처럼 사용하면 데이터를 가공하여 관리할 수 있다. 한 가지 주의할 점은 onError, onSuccess, select의 경우에 enabled 등의 옵션과 상관 없이 useQuery가 호출되는 경우 무조건적으로 호출된다는 점이다. 처음에는 이를 몰랐고 select 안에서 사용되는 modal이 여러 번 반복되는 현상을 겪게 됐다. API 호출은 1번만 정상적으로 이뤄지고 있었기 때문에 다른 원인이라 생각해서 한참을 헤매고 나서야 이유를 알 수 있었다.</p>
<p>useQuery의 여러 옵션은 <a href="https://react-query.tanstack.com/">공식문서</a> 를 통해 볼 수 있지만 몇가지 중요한 옵션은 짚고 넘어가고자 한다. </p>
<blockquote>
<p>✅ cacheTime : 어느 시점까지 메모리에 데이터를 저장해 캐싱할 것인지를 결정하는 옵션이다. 기본값은 5분이다. (단위 ms)<br>✅ staleTime : React Query에서는 데이터를 요청한 쿼리를 stale하다고 판단하는데 이를 결정하는 시간을 결정하는 옵션이다. 기본 값은 0이다.<br>     (✨ cacheTime 이나 staleTime 둘 중에 하나라도 만료가 되면 데이터를 재요청하게 된다.)<br>✅ refetchOnMount : 컴포넌트가 마운트 되는 경우에 새로운 데이터를 가져온다. 기본값 true이다.<br>✅ enabled : false로 설정할 경우 컴포넌트가 마운트 되어도 데이터를 가져오지 않는다. 기본값은 true이다.<br>     (주로 동기적으로 데이터를 가져오고 싶은 경우에 사용할 수 있다.)<br>✅ refetchOnWindowFocus : 브라우저로 포커스가 옮겨진 경우에 새로운 데이터를 가져온다. 기본값은 true이다.<br>     (웹뷰 등을 사용한다면 이를 이용해 사용성을 개선할 수 있다.)</p>
</blockquote>
<p>여기서 <strong>cacheTime</strong> 과 <strong>staleTime</strong> 의 경우에는 React Query를 사용할 때 중요한 옵션이라고 생각한다. 캐싱 관련 기능을 손쉽게 사용할 수 있도록 도와주는 옵션들이기 때문에 이를 이용해 효율적으로 Server State를 관리할 수 있다. </p>
<h4 id="usemutation">useMutation</h4>
<p>서버의 데이터를 업데이트하는 작업을 진행하는 경우에는 useMutation 훅을 사용할 수 있다. </p>
<pre><code class="language-javascript">const { mutate: addList, isLoading } = useMutation(addData, {
    onSuccess: () =&gt; {
        invalidateListQuery();
        openSuccessToast({ message: &#39;저장되었습니다.&#39; });
    },
    onError: () =&gt; {
        openErrorToast({});
    },
});

const handleConfirm = async () =&gt; {
    addList(data);
}</code></pre>
<p>useMutation 역시 isLoading 이나 isError 등의 반환 값을 받아 처리할 수 있으며 추가로 mutate 메서드를 받을 수 있다. 이를 이용해서 서버의 데이터를 업데이트 할 수 있다. 이 때, 서버의 데이터를 업데이트 한 이후에 캐싱된 데이터도 새롭게 업데이트를 해야 하는데 invalidateQueries 메서드를 통해서 쉽게 해결할 수 있다. </p>
<pre><code class="language-javascript">// 어떤 키도 전달하지 않으면 캐시된 모든 쿼리를 무효화 할 수 있다.
queryClient.invalidateQueries()

// 키를 전달한 경우에는 해당 키를 가진 쿼리를 무효화 한다.
queryClient.invalidateQueries(&#39;list&#39;)</code></pre>
<p>추가로 onMutate 옵션을 통해서 mutate 함수가 실행되기 전에 필요한 로직을 작성할 수도 있다. </p>
<h2 id="마무리">마무리</h2>
<p>가장 최근에 사용했던 코드에 React Query를 적용해보면서 굉장히 많은 상태를 줄일 수 있다는 점을 느꼈다. 특히, 간단하게 사용을 해봤음에도 캐싱이나 여러가지 상태를 제공해주는 것의 강력함을 느낄 수 있었다. 특히, 기존의 많은 통신들을 React Query를 통해서 개선할 수 있다면 Redux store의 영역을 줄이고 modal의 open/close 의 상태나 다른 UI 관련 상태에만 집중할 수 있어 더욱 깔끔하게 코드를 개선할 수 있겠다는 생각이 들었다. 물론, 도입을 해보면서 Api 핸들링 부분이 기존과 맞지 않는 부분이 있어 어려움이 있었고 persist 기능 등 더욱 살펴볼 지점은 분명히 있었지만 매우 매력적인 도구라는 것은 분명했다. 다음에는 React Query를 프로젝트에 적용하면서 겪었던 어려움을 해결하는 과정에 대해서 기록해보고자 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React | Axios를 이용하여 파일 다운로드 구현하기]]></title>
            <link>https://velog.io/@jun_n3/React-Axios%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%ED%8C%8C%EC%9D%BC-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jun_n3/React-Axios%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%ED%8C%8C%EC%9D%BC-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 18 May 2022 12:46:14 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가며">들어가며</h1>
<p>현재, 현장에서 일하는 직원들을 위한 협업툴을 서비스하는 회사에서 개발을 하고 있다. 입사 초기에는 기존 서비스를 리액트로 마이그레이션 하는 작업을 천천히 맡아 진행하고 있었는데, SAAS 부분을 강화하는 프로젝트를 진행하게 되면서 운영 어드민을 새롭게 리드해서 만들게 되었다. 운영 어드민의 경우에 여러가지 핵심 비지니스 로직들이 많아서 부담이 되는 부분도 있었지만, 다양한 데이터를 다뤄보고 비지니스 로직을 구현할 수 있어 즐겁게 진행하는 중이다. 그 중에 결제 관련 기능을 구현하면서 인보이스, 청구서 다운로드 기능을 구현하게 됐다. 기존에 URL을 통해서 쉽게 다운로드를 구현할 수 있구나 싶었는데, 전과는 다른 방법을 이용해야 했기 때문에 기록으로 남겨두려고 한다. API 호출했는데 이상한 문자만 와요! 라는 부끄러운 소리를 했던 기억을 되돌려보며... 똑똑히 기억해야겠다.</p>
<h2 id="blob">Blob?</h2>
<p>부끄럽게도 그 동안 짧은 개발기간을 보내오는 동안 blob을 다뤄본 기억이 없었다. console을 통해 만나본 response 속에 이상한 문자가 바로 blob의 형태이다. blob은 Binary Large Object의 약자로 이름에서 예상이 되는 것처럼 바이너리 형태의 큰 객체(이미지, 비디오 등)를 담을 수 있다. 자바스크립트에서는 이를 사용하기 위해 new Blob을 사용할 수 있다.</p>
<h2 id="어떻게-적용할까">어떻게 적용할까?</h2>
<p>우선 blob을 이용하기 위해서는 Axios에서 responseType을 설정해야 한다.</p>
<pre><code class="language-javascript">export const downloadFile = async (
) =&gt; {
    return receive(
        client(
           url,
            {
                method: &#39;GET&#39;,
                responseType: &#39;blob&#39;,
            }
        )
    );
};</code></pre>
<p>위처럼 responseType 을 지정한 후에는 response를 이용해서 가공을 진행하면 된다.</p>
<pre><code class="language-javascript">
// Blob은 배열 객체 안의 모든 데이터를 합쳐 blob으로 반환하기 때문에 []안에 담는다!
const blob = new Blob([res.data])

// window 객체의 createObjuctURL을 이용해서 blob:http://~~~ 식의 url을 만들어 준다.
const fileUrl = window.URL.createObjectURL(blob);

// link 안에 위에서 만든 url을 가지고 있는 a 태그를 만들고 보이지 않도록 해준다.
// a 태그는 노출하지 않고 자동으로 클릭되도록 할 예정! 
const link = document.createElement(&#39;a&#39;);
link.href = fileUrl;
link.style.display = &#39;none&#39;;</code></pre>
<p>지금까지의 과정을 간단히 설명하자면 blob 객체를 만들고 이를 통해서 url을 생성한다. 이 url을 가지고 있는 a태그를 만들고<br>이를 클릭해서 실행되도록 하면 된다. 다만, 파일 이름을 지정해줘야하는 단계가 남아있는데 나의 경우에는 파일 이름이 header에<br>&quot;content-disposition&quot; 를 통해 담겨오기는 했지만 인코딩 되어 있었기 때문에 디코딩이 필요했다. 코드는 아래와 같았다.</p>
<pre><code class="language-javascript">
// response의 headers에 있는 content-disposition을 찾아서 디코딩 한다.
const injectFilename = (res: ApiResponse) =&gt; { 
      const disposition = res.headers[&#39;content-disposition&#39;];

      const fileName = decodeURI(
        disposition
          .match(/filename[^;=\n]*=(([&#39;&quot;]).*?\2|[^;\n]*)/)[1]
          .replace(/[&#39;&quot;]/g, &#39;&#39;)
      );
        return fileName;
    };</code></pre>
<p>마지막으로, a 태그의 다운로드 프로퍼티에 만들어 준 이름을 넣어주고 클릭되도록 하면 된다.</p>
<pre><code class="language-javascript">link.download = injectFilename(res);

document.body.appendChild(link);
link.click();
link.remove();

window.URL.revokeObjectURL(fileUrl);</code></pre>
<p>위의 코드를 적용하면서 처음에는 에러가 많이 났다. 파일 이름도 테스트라고 입력 했다가 확장자를 제대로 입력하지 않아서 txt 파일로 받아지기도 하고<br>파일을 열어보니 알 수 없는 문자가 가득한 외계어의 파일이 열리기도 하고... 이름도 완벽하고 확장자도 완벽했지만 자꾸만 열 수 없다는 알림이 뜨면서 확인이 되지 않기도 했다.</p>
<p>이상한 문자의 파일이 자꾸 뜨는 오류의 경우는 파일 이름을 정확하게 입력하면서 해결이 됐고, 파일이 손상되서 열 수 없는 이유는 다른 코드를 수정하면서<br>responseType을 지워버렸기 때문이었다. 그렇게 몇 번의 시도 끝에 정확하게 원하는 파일을 다운로드 할 수 있었다.</p>
<p>파일을 다운로드 하는 방법이 위와 같은 방법만 있지는 않다. 중간에 다른 방법을 이용해서 시도했고 성공하기도 했는데 나는 사용하지 않았지만 간단히 소개만 해보려고 한다.</p>
<h2 id="간편한-방법">간편한 방법</h2>
<p>나는 리액트를 사용하여 프로젝트를 진행하고 있고 API 통신의 경우 Axios를 사용하고 있기 때문에 form의 action를 사용하고 싶지 않았다. 하지만 form의 action을 사용하면 조금 더 간단하게 시도해 볼 수 있다.</p>
<pre><code class="language-javascript">&lt;form action={url} method=&quot;get&quot;&gt;
  &lt;button type=&quot;submit&quot;&gt;버튼&lt;/button&gt;
&lt;/form&gt;</code></pre>
<p>위와 같이 시도하면 된다. form의 action을 이용하기 위해서는 submit 이벤트를 이용해야 하고 이를 통해 새로고침이 발생하기 때문에 나는 target=&quot;_blank&quot;를 통해서 새 창이 열리도록 했고 다운로드를 받을 수 있었다.</p>
<p>이 방법을 시도해보면서 반성을 하게 됐는데, 그 동안 form에 있는 action의 기능을 전혀 사용해보지 않았다는 점이었다. react의 경우에 주로 onClick이나 onSumbit을 이용하면 거의 모든 기능 구현에 문제가 없기도 했기 때문에 고려조차 하지 않았는데, 때로는 훨씬 적은 코드로도 동일한 기능을 수행할 수 있다는 점을 알게 됐다.</p>
<p>물론, 새로고침 때문에 새 창을 이용하거나 API에 관련 된 로직 등을 흩어지도록 만들고 싶지 않아서 통일성을 위해 onClick 이벤트를 이용해 기능을 구현했지만 다른 문제를 접근할 때 편하고 익숙한 방법만 찾고 있지는 않는지 다시 한 번 생각해보게 된 계기가 됐다. 고작 5-6개월 정도 스타트업 개발자로서 즐겁게 일하고 있는데, 앞으로도 많은 문제를 만나고 이겨내면서 오래 오래 개발을 해나가고 싶다. 그 기간 동안 고이기 보다는 신선하고 효율적인 방법에 대해서 많이 고민해 볼 수 있다면 좋겠다.</p>
<h2 id="전체-코드">전체 코드</h2>
<p>아래는 편하게 볼 수 있도록 전체 코드이다.</p>
<pre><code class="language-javascript">const onClick = () =&gt; {
  downloadFile().then(res =&gt; {
    const blob = new Blob([res.data]) 

    const fileUrl = window.URL.createObjectURL(blob);

    const link = document.createElement(&#39;a&#39;);
    link.href = fileObjectUrl;
    link.style.display = &#39;none&#39;;

    const injectFilename = (res: ApiResponse) =&gt; { 
      const disposition = res.headers[&#39;content-disposition&#39;];

      const fileName = decodeURI(
        disposition
          .match(/filename[^;=\n]*=(([&#39;&quot;]).*?\2|[^;\n]*)/)[1]
          .replace(/[&#39;&quot;]/g, &#39;&#39;)
      );
        return fileName;
    };
    link.download = injectFilename(res);

    document.body.appendChild(link);
    link.click();
    link.remove();
  })
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 044 | 객체로 컴포넌트 활용하기]]></title>
            <link>https://velog.io/@jun_n3/TIL-044-%EA%B0%9D%EC%B2%B4%EB%A1%9C-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jun_n3/TIL-044-%EA%B0%9D%EC%B2%B4%EB%A1%9C-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 03 Oct 2021 12:30:52 GMT</pubDate>
            <description><![CDATA[<h1 id="1-객체로-컴포넌트-활용하기">1. 객체로 컴포넌트 활용하기</h1>
<p>React를 사용하여 프로젝트를 진행하면서 많은 고민이 되는 부분 중에 하나는 바로 어떤 값을 <code>state</code> 로 설정하여야 하는지에 대한 고민일 것이다. 만약 한 페이지 내에서 사용하는 모달창이 5개가 있고 이를 boolean 값을 통해 관리한다면 5개의 비슷한 <code>state</code> 가지고 있어야한다. 이는 매우 낭비라는 생각이 들었기 때문에 이를 개선해보고 싶었다. </p>
<h2 id="어떤-방법이-좋을까">어떤 방법이 좋을까?</h2>
<p>먼저, 어떤 값을 이용하면 5개의 상태값을 하나로 줄일 수 있을 지 고민했다. 이는 간단했는데, 바로 <code>boolean</code> 값이 아닌 설명하는 내용을 통해서 관리하면 되는 문제였다. </p>
<p>예를 들어 삭제 관련된 <code>Modal</code> 의 경우에는 <code>delete</code> 등의 이름을 부여했다. 그렇다면 이제 삼항연산자 등을 이용해서 이름에 맞는 <code>Modal</code> 이 뜨도록 하면 된다. 하지만, 문제는 <code>Modal</code> 이 2개가 아닌 여러 개라는 점이었다. </p>
<h2 id="switch-문-대신-객체-사용">Switch 문 대신 객체 사용</h2>
<p>각 케이스마다 해당하는 로직이 구현되도록 하는 방법으로는 <code>switch</code> 문이 있다. 하지만, <code>switch</code> 문의 경우에는 변수에 할당할 수 없기 때문에 이를 JSX 내에 사용하지 않는다. 그렇기 때문에 다른 방법이 필요한데, 이 때 객체를 사용할 수 있다. </p>
<pre><code class="language-js"> const MODAL = {
    delete: (
      &lt;DeleteModal
        id={openModal.id}
        handleCancel={handleCancel}
        updateComment={updateComment}
      /&gt;
    ),
    put: (
      &lt;ToMyBooksModal
        handleCancel={handleCancel}
        handleMakeButton={handleMakeButton}
        libraryList={libraryList}
        handleLibrary={handleLibrary}
        onPutBook={onPutBook}
      /&gt;
    ),
    make: (
      &lt;LibraryModal
        handleCancel={handleCancel}
        handleLibraryName={handleLibraryName}
        libraryName={libraryName}
        addLibrary={addLibrary}
      /&gt;
    ),
  };</code></pre>
<p>위는 <code>Modal</code> 을 한 번에 가지고 있는 객체인 <code>MODAL</code> 이다. 이를 통해서 각 상태값이 키값으로 들어올 때 원하는 <code>Modal</code> 를 불러올 것을 예상할 수 있다. </p>
<pre><code class="language-js"> &lt;Potal&gt;
   &lt;ModalLayout&gt;{MODAL[openModal.type]}&lt;/ModalLayout&gt;
 &lt;/Potal&gt;</code></pre>
<p>위의 코드처럼 사용하면 <code>Potal</code> 을 통해서 외부 DOM에 렌더링 하면서 이전에 작성한 <code>&lt;ModalLayout&gt;</code> 의 <code>children</code> 으로 <code>Modal</code> 이 들어가도록 할 수 있다. 반복을 줄일 수 있고 코드로 훨씬 간결해 진 것을 알 수 있다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 043 | 드래그 슬라이드 구현하기]]></title>
            <link>https://velog.io/@jun_n3/TIL-043-%EB%93%9C%EB%9E%98%EA%B7%B8-%EC%8A%AC%EB%9D%BC%EC%9D%B4%EB%93%9C-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-8338ccho</link>
            <guid>https://velog.io/@jun_n3/TIL-043-%EB%93%9C%EB%9E%98%EA%B7%B8-%EC%8A%AC%EB%9D%BC%EC%9D%B4%EB%93%9C-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-8338ccho</guid>
            <pubDate>Sun, 03 Oct 2021 12:11:03 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>프로젝트를 진행하면서 꽤 긴 시간을 사용했던 드래그 슬라이드에 대해서 정리해보고자 한다. 배워가는 단계에서 작성한 코드이기 때문에 구현에 초점을 맞췄다는 점을 짚고 넘어갔으면 한다. </p>
</blockquote>
<h1 id="드래그-슬라이드">드래그 슬라이드</h1>
<p><img src="https://images.velog.io/images/jun_n3/post/20861967-979e-4bb7-bd6a-6de0c1c8052a/Sep-17-2021%2001-22-52.gif" alt=""></p>
<p>위의 모습처럼 마우스를 통해 드래그를 했을 때 좌우로 움직이는 슬라이드이다. 단순히 <code>onDrag</code> 이벤트를 사용하면 되는 것이 아닌가 싶을 수 있지만 <code>drag</code> 이벤트를 사용하는 경우에 잔상이 생겨서 원하는 결과물과는 달랐다. <code>onDrag</code> 이벤트를 사용하면서 잔상을 없애는 방법을 여러가지 찾아봤지만 드래그를 제한하는 방법들이 대부분이었다. 그래서 아예 방향을 틀어서 <code>mouse</code> 관련 이벤트를 통해서 이를 구현했다. </p>
<h2 id="1-마우스-클릭-관련-이벤트">1. 마우스 클릭 관련 이벤트</h2>
<pre><code class="language-js">
const handleScroll = (e, scroll) =&gt; {
    switch (scroll) {
      case &#39;start&#39;: // 마우스 버튼 누르는 경우
        setOriginX(e.clientX);
        setIsScroll(true);
        break;
      case &#39;end&#39;: // 마우스를 버튼 누르기 중단
        setAfterX(position);
        setIsScroll(false);
        break;
      case &#39;leave&#39;: // 마우스가 영역을 벗어난 경우
        setIsScroll(false);
        break;
      default:
        break;
    }
  };
</code></pre>
<p>먼저 드래그를 하고 있는 상태를 확인하기 위해서 <code>onMouseDown</code> 과 <code>onMouseUp</code> 이벤트를 사용했다. 이 때 여러 개의 함수를 만들기 보다는 스크롤 여부를 추적하는 한 개의 함수를 사용하기를 원했고 <code>switch</code> 문을 통해서 이를 작성했다. </p>
<p>여기서 <code>originX</code> 는 드래그를 시작하는 지점의 위치를 저장할 변수이고 <code>afterX</code> 는 드래그를 중단한 경우에 마지막 위치를 저장할 변수이다. 이를 통해서 중간에 드래그를 멈췄다가 다시 시작하는 경우에 초기화 되지 않도록 했다. </p>
<h2 id="2-스크롤-관련">2. 스크롤 관련</h2>
<pre><code class="language-js">const handleSlide = e =&gt; {
    const newPosition = e.clientX - originX + afterX;
    const hiddenLength = e.currentTarget.offsetWidth - 166 * BOOKS.length;
    if (isScroll === false) {
      return;
    }

    newPosition &lt; 10 &amp;&amp; newPosition &gt; hiddenLength &amp;&amp; setPosition(newPosition);
  };</code></pre>
<p>이제 마우스가 움직일 때 따라서 드래그 될 수 있도록 <code>onMouseMove</code> 이벤트를 이용해서 위치를 변동할 수 있도록 했다. 이 때, 좌우 스크롤의 경우에 빈 공간을 보여주지 않도록 제한사항을 두도록 했다. </p>
<h1 id="구현-화면">구현 화면</h1>
<p><img src="https://images.velog.io/images/jun_n3/post/faa86391-5ee6-42ee-93e6-9de2a2eeb023/Sep-17-2021%2001-22-52.gif" alt=""></p>
<p>이를 구현하는데 꽤 많은 시간을 썼다. 특히, 초기화 되는 부분에서 애를 많이 썼던 것 같다. 구현 방법을 구상하고 이를 코드로 옮기는데 얼마나 부족한지 많이 깨닫게 된 기능이었다. 누군가가 비슷한 기능을 만들 때 조금은 도움이 됐으면 하는 마음으로 코드를 남겨본다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React | 밀리의 서재 클론 프로젝트 회고록 ✨]]></title>
            <link>https://velog.io/@jun_n3/React-%EB%B0%80%EB%A6%AC%EC%9D%98-%EC%84%9C%EC%9E%AC-%ED%81%B4%EB%A1%A0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0%EB%A1%9D</link>
            <guid>https://velog.io/@jun_n3/React-%EB%B0%80%EB%A6%AC%EC%9D%98-%EC%84%9C%EC%9E%AC-%ED%81%B4%EB%A1%A0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0%EB%A1%9D</guid>
            <pubDate>Sun, 03 Oct 2021 12:09:07 GMT</pubDate>
            <description><![CDATA[<h1 id="📋-project-overview">📋 Project Overview</h1>
<blockquote>
<p><a href="https://github.com/wecode-bootcamp-korea/24-2nd-TomorrowsNotebook-frontend">TomorrowsNotebook - Frontend Github</a>
<a href="https://github.com/wecode-bootcamp-korea/24-2nd-TomorrowsNotebook-backend">TomorrowsNotebook - Backend Github</a>
<a href="https://youtu.be/WJS8kuzjzJU">시연 영상 - 유튜브</a></p>
</blockquote>
<h2 id="span-stylecolor0066ff프로젝트-소개span"><span style="color:#0066ff">프로젝트 소개</span></h2>
<p>&#39;밀리의 서재&#39; 어플리케이션을 벤치마킹하여 전자책 구독 플랫폼 서비스를 구축했습니다.
애자일 방법론을 채택하여 연휴를 제외하고 총 2주 간 스프린트 방식으로 프로젝트를 진행했습니다. 사용자에게 편리하고 유용한 서비스를 제공할 수 있는 방법을 고민하고 실행에 옮기는 것을 기초 목적으로 삼았으며, 효율적인 아키텍처 구축을 위해 노력했습니다.</p>
<p>백엔드팀에서는 사용자 중심의 다양한 서비스를 제공하기 위해 효율적인 데이터 모델링 방안을 고민했습니다. 최적화된 데이터베이스 사용을 목적으로 장고 ORM을 사용하였고, AWS의 RDS, EC2, S3버킷 등을 사용하여 시스템 아키텍쳐를 설계했습니다. 각 기능에 대해 유닛테스트를 진행하여 여러 환경 변수에 대한 안정성을 확보했습니다.</p>
<p>프론트엔드팀에서는 React Hooks와 Styled-Component, 라이브러리 등을 이용하여 사용자의 흥미를 이끌어낼 수 있는 UI/UX를 구현했습니다. 직관적인 구조 파악과 재사용성을 염두에 두고 컴포넌트를 분리시켰으며, 동시에 API 통신으로 받은 데이터의 효율적 전달성을 확보했습니다.</p>
<h2 id="span-stylecolor0066ff제작-기간span"><span style="color:#0066ff">제작 기간</span></h2>
<p><strong>2021.09.13(월) ~ 2021.10.1(금) (연휴기간 제외)</strong></p>
<h2 id="span-stylecolor0066ff기술-스택span"><span style="color:#0066ff">기술 스택</span></h2>
<p>*<em>✨ 프론트엔드(3명) : 강연옥, 조성환, 주철진 *</em></p>
<ul>
<li>JavaScript(ES6+)</li>
<li>React</li>
<li>Styled Component</li>
<li>Styled-icon</li>
<li>React-pdf</li>
</ul>
<p>*<em>🔥 백엔드(3명) : 이무현, 신우주, 박지원 *</em></p>
<ul>
<li>Django</li>
<li>Python</li>
<li>MySQL</li>
<li>Aquery</li>
</ul>
<p><strong>공통 협업 도구</strong></p>
<ul>
<li>Slack </li>
<li>Notion </li>
<li>Trello</li>
<li>Git</li>
<li>Github</li>
<li>Zoom</li>
</ul>
<h2 id="span-stylecolor0066ff주요-구현-사항span"><span style="color:#0066ff">주요 구현 사항</span></h2>
<p>📕 는 내가 구현한 기능에 표시했다. </p>
<p>✅ 로그인 페이지</p>
<p>✨ 로그인 페이지 레이아웃
✨ 카카오 API를 이용한 소셜로그인 구현
✨ 로그인 성공시 페이지 이동 구현</p>
<p>✅ 메인 페이지</p>
<p>📕 메인페이지 레이아웃 구현
📕 메인페이지 배너 캐러셀 슬라이드 구현
📕 책 리스트 스크롤 슬라이드 구현
📕 메인페이지 API 통신을 이용한 책 추천 서비스 구현(최신, 출판사별)
📕 상세페이지로 동적라우팅 기능 구현</p>
<p>✅ 검색창 페이지</p>
<p>✨ 카테고리별 도서 검색 기능
✨ 검색 필터링
✨ 검색어에 따른 결과 미리보기 창
✨ 검색 결과창에서 도서 클릭 시 상세페이지로 이동</p>
<p>✅ 뷰어 페이지</p>
<p>✨ react-pdf 라이브러리 사용하여 PDF 파일 불러오기
✨ S3 CORS 에러 및 객체 퍼블릭 설정 변경 통한 디버깅
✨ 목차 및 해당 페이지로 이동 기능
✨ 페이지 이동 기능
✨ 한 쪽 보기, 두 쪽 보기 기능
✨ 페이지 스크롤 기능</p>
<p>✅ 책 상세 페이지</p>
<p>📕 상세 페이지 레이아웃 및 API 통신(동적라우팅)
📕 리뷰 추가 및 삭제
📕 리뷰 좋아요 기능
📕 내 서재에 담기 기능
📕 책장 만들기 기능
📕 바로 읽기 기능</p>
<p>✅ 나의 서재 페이지</p>
<p>✨ 책장 만들기/삭제 기능
✨ 읽은 책 담기 기능
✨ 책장 속 서재 필터링 기능</p>
<hr>
<h1 id="✨-project-review">✨ Project Review</h1>
<h2 id="span-stylecolor0066ff좋았던-점span"><span style="color:#0066ff">좋았던 점</span></h2>
<h3 id="명확한-기록과-유연한-소통">명확한 기록과 유연한 소통</h3>
<p>이번 2차 프로젝트를 진행하면서 부담이 되는 부분이 많았다. 백엔드의 경우에는 데이터 테이블이 복잡할 수 밖에 없었고 프론트의 경우에는 어플리케이션이다 보니 웹으로 전환하는데 어려움이 많을 것으로 예상되었다. 그렇기에 초반에 소통을 하는데 많은 신경을 썼다. 각 화면의 부분들을 캡처해서 보면서 데이터 통신을 어떻게 진행할지 구체적으로 이야기했고 서로의 생각이 다른 부분의 경우에는 여러 사이트들을 직접 보면서 열띤 토론을 이어나갔다. 단지, 누가 편하기 위해서가 아닌 최대한 사용자에게 편한 환경을 제공하고자 하는 토론이었기에 즐겁게 임할 수 있었다. 그렇기에 각 기능들이 만족스러운 결과로 나올 수 있었다.</p>
<h3 id="새로운-기술에-대한-적극적인-도전">새로운 기술에 대한 적극적인 도전</h3>
<p>프론트엔드의 경우에는 2차 프로젝트를 진행하면서 리액트 <code>Hooks</code>와 <code>Styled Component</code>를 사용해야 했다. 모두 처음 사용해보는 기술들이다보니 초반 레이아웃을 구성하면서 어려움이 많았다. 마치, 처음 <code>JavaScript</code> 를 구현했던 기분이 느껴졌다. 그럼에도 두려움 보다는 흥미로 접근하려고 노력했고 한 줄 한 줄 코드를 늘려나갈 수 있었다. 이번 프로젝트에서 기억에 남았던 부분은 내 코드만 보며 리뷰를 반영했던 것이 아니라 다른 분들의 코드에 달린 리뷰들을 반영해보고자 했던 시도였다. 특히 <code>Potal</code> 에 관련된 내용의 경우에는 우연히 이야기를 듣고 흥미가 생겼었는데 다수의 <code>Modal</code>을 한 페이지 내에서 구현하게 되면서 매우 유용하게 사용할 수 있었다. 단순히, 전에 하던 방식으로 구현하기 보다는 여러 리뷰와 검색을 통해서 새로운 기술을 적극적으로 도입하려고 했던 기억이 인상 깊다. </p>
<h2 id="span-stylecolor0066ff아쉬운-점span"><span style="color:#0066ff">아쉬운 점</span></h2>
<h3 id="블로커에-대한-대처">블로커에 대한 대처</h3>
<p>프로젝트는 나 혼자만의 문제보다 동료들의 문제를 공유하고 함께 해결해나가는 과정이 중요하다는 생각이 들었다. 이번 프로젝트의 경우 프론트와 백엔드 모두 새로운 도전들이 있었고 그로 인해서 여러가지 블로커들이 생길 수 밖에 없었는데, 이에 대한 해결 과정이 조금은 미숙했다는 생각이 든다. 그러다보니 데이터 통신을 위해 필요한 시간들이 늦어질 수 밖에 없었고 에러를 고쳐나가는 과정이 급할 수 밖에 없었다. 긴밀하게 소통하면서 맞춰나가고 있다고 생각했지만 블로커에 대한 구체적인 대책을 세우는 부분에 있어서는 조금 아쉬움이 남는다. 그럼에도 서로가 어떤 것들을 진행하고 있는지 매일 스탠드업 미팅을 통해서 잘 공유가 됐기 때문에 좋은 결과물을 만들 수 있었다. </p>
<h3 id="추가-구현에-대한-아쉬움">추가 구현에 대한 아쉬움</h3>
<p>아무래도 아쉬움이 남을 수 없는 부분은 추가 구현에 대한 부분이라고 생각한다. 우리는 밀리의 서재를 모티브로 프로젝트를 진행했는데, 시간이 넉넉하지 못하다보니 필수적인 사항들을 위주로 구현을 진행했다. 그렇기 때문에 사이트를 이용해보면서 느꼈던 불편함에 대해서 정리하고 이를 개선하는 과정에 대한 아쉬움이 남는다. 리뷰 삭제나 상세 페이지에서 책장을 만들고 책을 추가하는 과정에 있어서 불편했던 부분들을 개선하기는 했지만 사이트 전반적으로 사용자가 책을 선택하고 읽는 과정에 대해서 깊이 고민해보고 이를 개선할 수 있었다면 더 좋았겠다는 아쉬움이 남는다. 추후에 코드 리팩토링을 진행하면서 개선할 수 있는 부분이 있는지 고민해보는 과정을 가져보려고 한다. </p>
<h2 id="span-stylecolor0066ff프로젝트-후기span"><span style="color:#0066ff">프로젝트 후기</span></h2>
<p>2주라는 시간이 또 금방 지나가버렸던 2차 프로젝트가 마무리 됐다. 유일하게 어플리케이션을 가지고 프로젝트를 진행해야하기 때문에 걱정을 많이 했었다. 내가 발표했던 사이트였기 때문에 PM의 역할까지 진행을 했었는데, 사실 따로 PM은 없었고 모두가 PM처럼 진행했던 프로젝트라고 생각한다. 모두의 높은 책임감 속에서 어려운 환경에서도 다들 원했던 만큼 구현을 마무리 할 수 있었다. </p>
<p>특히 연휴에 코딩에 집중을 하고 싶어서 카페를 갔었는데 팀원들이 먼저 함께 하고 싶다며 연락이 와서 모일 수 있었다. 누군가가 &quot;해야한다!&quot; 하면서 모인 것이 아니라 &quot;하고 싶다&quot; 라고 모였던 그 순간이 정말 기억에 많이 남는 것 같다. 정말로, 함께하고 있구나 느낄 수 있었다. </p>
<p>명확하게 같은 방향을 바라보며 달려갈 수 있도록 해줬던 <strong>연옥</strong>님, 맡은 역할을 언제나 최선을 다해서 해결하는 <strong>성환</strong>님, 어려운 기능을 맡았고 걱정도 많이 하셨지만 행복하게 해낸 <strong>지원</strong>님, 분위기 메이커 역할만 아니라 새벽까지도 공부하며 원하던 기능을 구현해낸 <strong>우주</strong>님, 마지막으로 든든하게 뒤를 받쳐주며 어려운 역할을 해주신 <strong>무현</strong>님까지. </p>
<p>어려운 프로젝트임에도 언제나 웃으면서 즐겁게 할 수 있도록 해준 팀원들에게 정말 감사하다 ✨ 모두가 있었기에 내일배움공책이 있을 수 있었다 🤭</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 042 | Styled-Component]]></title>
            <link>https://velog.io/@jun_n3/TIL-042-Styled-Component</link>
            <guid>https://velog.io/@jun_n3/TIL-042-Styled-Component</guid>
            <pubDate>Sun, 19 Sep 2021 03:57:57 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>리액트를 사용하면서 <code>SASS</code> 를 사용하게 됐다. 기존의 CSS 만을 사용하는 경우에 비해서 nesting으로 인한 편리함이 마음에 들었다. 또, class 명 역시 비교적 길어지지 않게 작성할 수 있어서 편리했다. 하지만, 여전히 class 명에 대한 고민은 존재했고 동적인 효과를 위해서는 코드를 여러번 작성해야하는 경우도 많았다. 이를 해결하기 위해서 등장한 것이 <code>Styled-Component</code>이다.</p>
</blockquote>
<h1 id="styled-component">Styled Component</h1>
<p><code>Styled Component</code> 는 위에서도 말했던 것처럼 여전히 존재하는 문제들을 해결하기 위해서 고안됐다. </p>
<ul>
<li>CSS 클래스 명에 대한 고민은 여전합니다.<ul>
<li>ex. BEM (<code>.block__element--modifier</code>, <code>button button--state-success</code>)</li>
</ul>
</li>
<li>정해진 가이드가 없으면 구조가 복잡해집니다.</li>
<li>방대한 스타일 정보로 인한 스크롤 지옥도 여전합니다.</li>
<li>여전히 CSS 클래스로 조건부 스타일링을 하고 있습니다.</li>
<li>CSS 클래스로 조건부 스타일링을 하더라도 동적인 변화를 표현하기에 한계가 있습니다.<ul>
<li>ex. 1초에 px 값을 1씩 증가 → Inline 스타일에 의존</li>
</ul>
</li>
</ul>
<p>실제로 <code>SASS</code> 를 사용하면서 편리함을 느꼈지만 여전히 존재했던 문제들이다. 특히 프로젝트를 진행하면서 4가지 버전의 레이아웃을 동적으로 구현했어야 했는데 이 경우에 코드가 상당히 길어지는 일을 겪었다. 또한, 동적인 효과를 위해서 class를 사용하다보니 제약이 생기는 경우도 많았다. </p>
<p>그렇기 때문에 이제는 <code>JS</code> 를 통해서 <code>CSS</code> 까지 함께 할 수 있는 방법인 <code>Styled Component</code>가 나오게 됐다.</p>
<h2 id="사용-예시">사용 예시</h2>
<pre><code class="language-js">const Button = styled.button`
  position: absolute;
  top: 50%;
  ${props =&gt; (props.direction === &#39;left&#39; ? &#39;left:0&#39; : &#39;right:0&#39;)};
  font-size: 1.5rem;
  transform: translateY(-50%);
`;</code></pre>
<p>위의 경우를 보자. 동일한 <code>Button</code> 이라는 이름을 가진 요소이지만 <code>props</code> 으로 전달받은 <code>direction</code> 에 따라서 다른 스타일을 가지는 것을 알 수 있다. </p>
<pre><code class="language-js">const List = styled.div`
  position: absolute;
  width: 100%;
  height: 400px;
  padding: 40px 0 0 0;
  background: ${props =&gt; props.background};
  opacity: ${props =&gt; (props.isCurrentSlide ? 1 : 0)};
`;</code></pre>
<p>또한, 위의 경우처럼 해당 요소의 상태에 따라서 자동으로 동적인 스타일을 부여할 수도 있다. 위 코드를 보면 <code>isCurrentSlide</code> 라는 값을 통해서 현재 보고 있는 슬라이드인 경우에는 <code>opacity</code> 가 1이며 아닌 경우에는 0이 되는 것을 알 수 있다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React | 라인 프렌즈샵 클론 프로젝트✨]]></title>
            <link>https://velog.io/@jun_n3/React-%EB%9D%BC%EC%9D%B8-%ED%94%84%EB%A0%8C%EC%A6%88%EC%83%B5-%ED%81%B4%EB%A1%A0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8</link>
            <guid>https://velog.io/@jun_n3/React-%EB%9D%BC%EC%9D%B8-%ED%94%84%EB%A0%8C%EC%A6%88%EC%83%B5-%ED%81%B4%EB%A1%A0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8</guid>
            <pubDate>Sun, 12 Sep 2021 16:42:44 GMT</pubDate>
            <description><![CDATA[<h1 id="📋-project-overview">📋 Project Overview</h1>
<blockquote>
<p><a href="https://github.com/wecode-bootcamp-korea/24-1st-DotFriends-frontend">Dot Friends - Frontend Github</a>
<a href="https://github.com/wecode-bootcamp-korea/24-1st-DotFriends-backend">Dot Frineds - Backend Github</a>
<a href="https://youtu.be/T5bOgE7dzwk">시연 영상 - 유튜브</a></p>
</blockquote>
<h2 id="span-stylecolor0066ff프로젝트-소개span"><span style="color:#0066ff">프로젝트 소개</span></h2>
<p>우리는 유명한 커머스 사이트 중에 하나인 라인프렌즈 샵의 클론 코딩을 하게 됐다. 라인프렌즈 샵은 2011년, 모바일 메신저 &#39;라인&#39; 의 스티커 캐릭터로 탄생한 라인프렌즈는 다양한 캐릭터와 아티스트와의 콜라보를 활발히 이어가고 있는 기업으로서 커머스 사이트의 구성과 기본 기능에 대해서 익히기에 적합하다는 생각이 들었다. </p>
<h2 id="span-stylecolor0066ff제작-기간span"><span style="color:#0066ff">제작 기간</span></h2>
<p><strong>21.08.30 (월) ~ 21.09.10 (금)</strong></p>
<h2 id="span-stylecolor0066ff기술-스택span"><span style="color:#0066ff">기술 스택</span></h2>
<p>*<em>✨ 프론트엔드(3명) : 금보배, 박은정, 주철진 *</em></p>
<ul>
<li>HTML/CSS</li>
<li>JavaScript(ES6+)</li>
<li>React</li>
<li>SASS</li>
</ul>
<p>*<em>🔥 백엔드(3명) : 서동규, 신우주, 김동준 *</em></p>
<ul>
<li>Django</li>
<li>Python</li>
<li>MySQL</li>
<li>Aquery</li>
</ul>
<p><strong>공통 협업 도구</strong></p>
<ul>
<li>Slack </li>
<li>Notion </li>
<li>Trello</li>
<li>Git</li>
<li>Github</li>
<li>Zoom</li>
</ul>
<h2 id="span-stylecolor0066ff주요-구현-사항span"><span style="color:#0066ff">주요 구현 사항</span></h2>
<p>🌈 는 내가 구현한 기능에 표시했다. </p>
<p>✅ 회원가입 페이지</p>
<p>✨ 회원가입 페이지 레이아웃
✨ API 통신을 이용한 회원가입 구현
✨ 입력값에 따른 유효성 검사
✨ 입력 상황에 따라 사용자에게 적합한 내용을 입력하도록 안내 기능 구현</p>
<p>✅ 로그인 페이지</p>
<p>✨ 로그인 페이지 레이아웃
✨ access token을 이용한 로그인 기능 구현 
✨ 입력값에 따른 유효성 검사
✨ 로그인 성공시 페이지 이동 구현</p>
<p>✅ 메인 페이지</p>
<p>✨ 메인페이지 레이아웃 구현
✨ 메인페이지 캐러셀 슬라이더 기능
✨ 메인페이지 API 통신을 이용한 세일, 신상품 품목 렌더링</p>
<p>✅ 상품 리스트 페이지</p>
<p>🌈 API 통신을 통해 각 카테고리 몇 상품 리스트 구현
🌈 query string을 이용한 필터기능 구현(인기도순, 낮은가격, 높은 가격, 최신 등록)
🌈 동적 class 를 이용한 4가지 레이아웃 기능
(리스트뷰, 이미지뷰, 큰 이미지뷰, 갤러리뷰)
🌈 offset을 이용한 페이지네이션 기능
🌈 페이지 사이즈 관련 기능(10개씩 보기, 20개씩 보기, 30개씩 보기)
🌈 동적라우팅 이용한 상세페이지 이동 구현
🌈 access token 을 이용한 사용자 인증 및 찜하기 기능 구현
(로그인 유저만 찜하기 가능 및 유저에 따라 다른 찜하기 상태를 보여줌)
🌈 간략보기 버튼 클릭 시 모달창으로 상품 상세 미리보기 구현</p>
<p>✅ 상품 상세 페이지</p>
<p>🌈 API 통신을 통해 해당하는 상품의 상세 페이지 구현
🌈 상품의 수량 조절 기능 구현</p>
<p>✅ 하단 footer</p>
<p>✨ 레이아웃 구현</p>
<p>✅ 상단 Navbar</p>
<p>🌈 동적라우팅 이용한 카테고리에 맞는 리스트 페이지로 이동 구현
🌈 검색 기능 구현(query string 및 동적 라우팅 이용)
🌈 access token을 활용한 로그아웃 기능 구현</p>
<h2 id="span-stylecolor0066ff결과-화면span"><span style="color:#0066ff">결과 화면</span></h2>
<h3 id="1-회원가입">1. 회원가입</h3>
<p><img src="https://images.velog.io/images/jun_n3/post/a10557b1-58e7-4af4-b215-fb2bf134fd4d/Sep-12-2021%2023-53-49.gif" alt=""></p>
<p>validation check를 통해서 사용자가 올바른 값을 입력할 수 있도록 유도했다. 여러가지 값을 입력 받고 이를 검증해야 되기 때문에 까다로웠지만 보배님께서 백엔드에만 검증을 맡기지 않고 프론트에서도 최대한 검증을 할 수 있도록 구현했다.</p>
<h3 id="2-로그인">2. 로그인</h3>
<p><img src="https://images.velog.io/images/jun_n3/post/4a1902fc-5ddc-4f42-8aa1-33d4782ada96/Sep-12-2021%2023-56-24.gif" alt=""></p>
<p>회원가입과 마찬가지로 validation check를 통해서 사용자의 입력값이 유효한 경우에만 로그인 버튼을 누를 수 있도록 했다. 마찬가지로 백엔드와 프론트엔드 양쪽에서 2중으로 유효성 검사가 진행되도록 했다. </p>
<h3 id="3-메인-상단">3. 메인 상단</h3>
<p><img src="https://images.velog.io/images/jun_n3/post/2b746e28-c7c9-4db2-8b1f-f001db3b5d79/Sep-13-2021%2000-08-54.gif" alt=""></p>
<p>백엔드 API를 통해서 데이터를 전달받아 이를 메인 상단에 보여줄 수 있도록 했다. <code>background-image</code>를 이용해서 이미지가 변경되면서 슬라이드 될 수 있도록 했고 애니메이션 효과를 통해서 단조로움을 벗어날 수 있었다.</p>
<h3 id="4-메인-상품-탭">4. 메인 상품 탭</h3>
<p><img src="https://images.velog.io/images/jun_n3/post/8eb74614-01c9-4ac9-aecd-2158459301c5/Sep-13-2021%2000-09-24.gif" alt=""></p>
<p>4개의 상품을 묶어서 보여줄 수 있도록 했다. 이 때, 신상품과 세일 상품이라는 카테고리의 정보를 랜덤하게 받아와서 사용자가 접속할 때마다 다른 상품을 보여줄 수 있도록 구현했다.</p>
<h3 id="5-상품-리스트---필터">5. 상품 리스트 - 필터</h3>
<p><img src="https://images.velog.io/images/jun_n3/post/6e07bac8-436c-4c16-9c35-636c7f4d20cf/Sep-13-2021%2000-19-51.gif" alt=""></p>
<p><code>state</code> 를 이용해서 각 필터에 해당하는 버튼을 누를 때 <code>query string</code> 을 통해서 새로운 정보를 받아올 수 있도록 했다. 백엔드에서 <code>query string</code> 을 이용한 API를 빠르게 만들어줘서 어렵지 않게 구현할 수 있었다. 모든 데이터를 받아서 프론트에서 정렬할 수 있겠다는 생각이 들기도 했지만 여러 데이터를 다루는 것이 커머스 사이트의 특징이라는 생각에 <code>query string</code> 을 이용하는 것이 낫다는 판단을 했다.</p>
<h3 id="6-상품-리스트---4가지-버전-레이아웃">6. 상품 리스트 - 4가지 버전 레이아웃</h3>
<p><img src="https://images.velog.io/images/jun_n3/post/08def4d5-7d43-43cf-afe1-a1dbf863f5f1/Sep-13-2021%2000-30-11.gif" alt=""></p>
<p><code>className</code> 을 동적으로 변화시켜서 4개의 버전의 레이아웃을 구현했다. 실제 사이트의 경우에는 <code>query string</code> 을 이용하는 것처럼 보였으나 여러번의 요청을 보내고 싶지 않았기 때문에 동적 <code>className</code> 을 사용하였다. </p>
<h3 id="7-상품-리스트---페이지네이션">7. 상품 리스트 - 페이지네이션</h3>
<p><img src="https://images.velog.io/images/jun_n3/post/9eb4539f-4b83-4678-9af0-8a809ade7fc7/Sep-13-2021%2000-34-44.gif" alt=""></p>
<p>다수의 상품을 한 번에 받아오는 경우에 시간이 많이 소요될 수 있기 때문에 이를 제어하기 위해서 페이지네이션 기능을 구현했다. <code>offset</code> 를 상품 수와 현재 페이지를 통해서 계산한 뒤에 전달하여 해당하는 상품만 통신해서 받아올 수 있도록 했다. </p>
<p><img src="https://images.velog.io/images/jun_n3/post/eccd3fc9-81de-45ad-9601-01eeaa000781/Sep-13-2021%2000-34-59.gif" alt=""></p>
<p>또, 한번에 보고자 하는 상품의 수량을 조절할 수 있도록 했다.</p>
<h3 id="8-상품리스트---찜하기--간략보기">8. 상품리스트 - 찜하기 &amp; 간략보기</h3>
<p><img src="https://images.velog.io/images/jun_n3/post/b433036c-b897-4e9b-b298-e7d736ae9205/Sep-13-2021%2000-50-26.gif" alt=""></p>
<p>로그인 시에 발급 받았던 <code>token</code> 을 이용해서 사용자에 맞는 찜하기 상황을 보여줄 수 있도록 했다. 또, 로그인한 유저의 경우에만 찜하기 기능을 사용할 수 있도록 했다. 이는 바로 반영되어서 찜하기가 눌린 상품의 경우 인기도 순으로 정렬할 때 더 높은 우선도를 갖게 된다.</p>
<p><img src="https://images.velog.io/images/jun_n3/post/30a6217f-585e-4e10-bee8-bdb748a51e6b/Sep-13-2021%2000-50-13.gif" alt=""></p>
<p>상품을 누를 경우에 상세페이지로 이동하게 되는데, 리스트를 계속 보면서 상품의 정보를 자세히 보고 싶은 유저를 위해서 <code>간략보기</code> 기능을 구현했다. </p>
<h3 id="9-네비바---검색-기능">9. 네비바 - 검색 기능</h3>
<p><img src="https://images.velog.io/images/jun_n3/post/b8ad1406-a9da-427a-9473-298de6308b51/Sep-13-2021%2000-57-15.gif" alt=""></p>
<p>네비바에서 각 카테고리를 누르면 해당하는 상품 리스트로 이동하게 구현했고, 검색어를 입력한 경우에는 해당하는 상품들을 보여줄 수 있도록 했다. <code>query string</code> 과 동적라우팅을 이용해야 했는데 처음에는 다소 헤맸지만 여러 시도 끝에 성공할 수 있었다. </p>
<h3 id="10-상세페이지">10. 상세페이지</h3>
<p><img src="https://images.velog.io/images/jun_n3/post/dc8449c1-4090-4f42-b4a4-54defcf5b777/Sep-13-2021%2000-58-11.gif" alt=""></p>
<p>동적라우팅을 통해서 상품 리스트 페이지에서 원하는 상품을 클릭한 경우에 이동할 수 있도록 했다. 옵션을 선택한 경우에만 상품의 수량을 조정할 수 있도록 했다. </p>
<hr>
<h1 id="✨-project-review">✨ Project Review</h1>
<h2 id="span-stylecolor0066ff좋았던-점span"><span style="color:#0066ff">좋았던 점</span></h2>
<h3 id="소통을-통한-구현-과정">소통을 통한 구현 과정</h3>
<p>우리 팀은 백엔드와 프론트엔드 사이에서의 의사소통에 큰 문제가 없었다고 생각한다. 코드를 작성하면서 미리 미치 <code>key</code> 값들을 맞춰보기 위해서 생각했고 백엔드에서 얻은 구조를 바탕으로 <code>Mock data</code> 를 만들어 사용했기 때문에 수정사항이 많이 생기지 않았다. </p>
<p>또한, 프론트엔드와 백엔드 사이에서 각 기능들을 구현하는 경우에 어떤 방식으로 동작하는 것이 옳을 지에 대해서 이야기 할 수 있는 시간들이 많았는데 한 쪽의 주장만 일방적으로 하는 것이 아니라 서로 납득할 수 있는 부분으로 협의하면서 만족스러운 결과를 낼 수 있었다. </p>
<p>특히 찜하기 기능의 경우에는 서로 의견을 많이 나눌 수 있었고 인증과 인가를 통해서 유저에 따른 다른 화면을 그리도록 할 수 있었다. </p>
<h3 id="목표는-작게-구현은-확실하게">목표는 작게, 구현은 확실하게</h3>
<p>PM이었던 동규님의 말처럼 우리 팀은 많은 목표를 설정하지 않았다. 구현할 수 있는 최소한을 설정하였고 이 부분은 최대한 확실하게 구현할 수 있도록 했다. 그렇기 때문에 프로젝트 기간동안 팀원들이 아프기도 하고 백신으로 인해서 생겼던 어려움도 있었지만 차근차근 목표들을 이뤄나갈 수 있었다. </p>
<p>또, 각자 맡은 페이지의 경우에는 사소해 보일지라도 최대한의 기능들을 구현하기 위해서 노력했고 그 마음만큼 괜찮은 결과를 냈다고 생각한다. </p>
<p>자꾸만 솟아나는 욕심과 부족한 내 실력 사이에서 힘들기도 했던 프로젝트 기간이었지만 같은 방향을 바라보고 함께하는 팀원들이 있었기에 힘내서 진행할 수 있었다.</p>
<h2 id="span-stylecolor0066ff아쉬운-점span"><span style="color:#0066ff">아쉬운 점</span></h2>
<h3 id="코드-리팩토링의-부족">코드 리팩토링의 부족...</h3>
<p>생각보다 내 속도는 느렸고 맡은 기능들을 구현하는데 꽤 긴 시간이 걸리게 됐다. 그러다보니 나중에는 코드의 질에 대해서 깊이 고민할 수 있는 시간이 부족했다. 빠르게 구현을 마무리하고 팀원들간에 코드 리뷰를 통해서 리팩토링을 할 수 있는 시간을 가질 수 있었다면 좋았을텐데 그러지 못한 점이 아쉽다. 그래도, 멘토님들의 지속적인 피드백을 통해서 부족했던 부분들을 채워나갈 수 있었던 점은 좋았다. </p>
<h3 id="긴밀한-소통의-필요성">긴밀한 소통의 필요성</h3>
<p>무난하게 소통이 진행되었다고 생각하기는 하지만, 서로 간의 긴밀한 소통의 경우에는 조금 부족했다는 생각이 든다. 특히 프론트엔드의 경우에는 각자 맡은 페이지를 최대한 잘 그려내기 위해서 시간이 많이 들었는데 그러다보니 서로의 진행사항이나 어려운 점에 대해서 빠르게 공유하는 것이 어려웠다. 주말에 만나서 함께 하거나 서로의 코드를 보고 개선하는 것을 돕기도 했지만 기술적인 것을 떠나 심적으로 더 공유하고 나눌 수 있었다면 좋았겠다는 생각이 든다. </p>
<p>특히 팀원분들이 아픈 상황에서도 맡은 역할에 대한 부담감을 가지고 있던 상황을 생각하면 마음 편히 이야기를 나눌 수 있도록 하는 역할을 하지 못했던 것 같아 미안한 마음이 든다. 마음 깊이 의지할 수 있는 동료 개발자는 어떤 사람인지에 대해서 생각해보는 계기가 되었다. </p>
<h2 id="span-stylecolor0066ff프로젝트-후기span"><span style="color:#0066ff">프로젝트 후기</span></h2>
<p>처음으로 진행했던 1차 프로젝트. 시작 전에는 정말 걱정이 많았는데 좋은 분들을 만나서 즐겁게 진행할 수 있었다. 진행하는 2주 동안은 잘하고 있는걸까? 이게 하나의 사이트가 될 수 있을까? 감이 오지 않았는데 서로의 결과물들이 합쳐져서 하나가 되었을 때는 정말 기분이 좋았다. </p>
<p>특히 여러 번, 서버를 열어달라고 이야기 하기도 하고 회의 때마다 해야할 것들에 대해서 중요하게 이야기 했던 것 같아서 미안한 마음이 큰데, 싫은 내색 없이 오히려 더 열심히 함께 해줬던 <code>DOT 프렌즈</code> 팀원들에게 정말 감사하다. </p>
<p>PM으로서 열정적으로 팀을 이끌고 격려해주었던 <strong>동규(보스)</strong>님, 즐거움 뿐만 아니라 든든하게 백엔드로서 지원해 줬던 <strong>우주(문)</strong>님, 많은 사색으로 좋은 API를 만들어줬던 <strong>동준(브라운)</strong>님, 항상 웃으며 성실하게 맡은 역할을 다 했던 <strong>은정(에드워드)</strong>님, 아이디어 뱅크에, 아파서 고생하기도 했지만 주말에도 만나 말한 것들은 다 해낸 <strong>보배(샐리)</strong>님까지. 각자의 공간에서 잘 지내자며 정했던 <code>DOT FRIENDS</code> 였지만 하나의 팀으로 좋았던 2주였고 단단했던 프로젝트였다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 041 | 표현식과 구문의 차이]]></title>
            <link>https://velog.io/@jun_n3/TIL-041-%ED%91%9C%ED%98%84%EC%8B%9D%EA%B3%BC-%EA%B5%AC%EB%AC%B8%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@jun_n3/TIL-041-%ED%91%9C%ED%98%84%EC%8B%9D%EA%B3%BC-%EA%B5%AC%EB%AC%B8%EC%9D%98-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Sun, 12 Sep 2021 13:23:06 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>JavaScript를 공부하면서 여러가지 문법에 대해서 공부를 했지만 if<strong>문</strong> 에서의 구문과 표현식의 차이에 대해서는 진지하게 고민해 본 적이 거의 없었다. 이번 프로젝트를 하면서 이에 대해서 크게 느낄 수 있었는데, 이에 대해 정리해보자.</p>
</blockquote>
<h1 id="if문과-삼항연산자">if문과 삼항연산자</h1>
<p>본격적으로 프로젝트를 하면서 받았던 많은 피드백 중에 하나가 <code>if, else</code> 문을 사용했던 내용을 삼항연산자로 바꿔보라는 것이었다. 얼핏 보기에도 삼항연산자를 사용했을 경우에 식이 간결해지기 때문에 가독성에 좋아보이기는 했지만 구체적인 이유에 대해서는 고민이 없이 사용을 했었다. </p>
<pre><code class="language-js">if(a &gt; 0){
  console.log(&quot;wow&quot;);
} else {
  console.log(&quot;no!!&quot;);
}</code></pre>
<p>위의 식은 삼항 연산자를 사용하면 아래처럼 간단해 진다.</p>
<pre><code class="language-js">a &gt; 0 ? console.log(&quot;wow&quot;) : console.log(&quot;no!!&quot;);</code></pre>
<p>무엇인가 간단해지고 좋은건 알겠는데 정확히 좋은 이유는 뭘까? </p>
<h2 id="구문과-표현식의-차이">구문과 표현식의 차이</h2>
<p>간단하게 설명하자면 구문의 경우에는 변수에 할당할 수가 없다. <code>JavaScript</code> 를 사용하는 사람이라면 아래처럼 사용하는 경우를 보지 못했을 것이다.</p>
<pre><code class="language-js">const print = if(a&gt;0){
                return &quot;wow&quot;;
              } else{
                return &quot;no!!&quot;;
              };</code></pre>
<p>당연히 위와 같은 방식으로 사용하는 경우는 없다. 다만 아래는 가능하다.</p>
<pre><code class="language-js">const print = a&gt;0 ? &quot;wow&quot; : &quot;no!!&quot;;</code></pre>
<p>이런 차이로 인해서 표현식의 경우에는 리액트의 <code>JSX</code> 안에서도 사용이 가능하며 변수에 할당하여 필요한 경우에도 자유롭게 사용할 수 있다.</p>
<h2 id="사용-예시">사용 예시</h2>
<p>1차 프로젝트를 진행하면서 <code>상세페이지</code> 에서 수량을 더하고 빼는 기능을 만들어야하는 경우가 있었다. 이를 더하는 함수와 빼는 함수 두개로 만들고 싶지 않아서 하나의 함수로 만들고 싶었는데 처음에는 아래와 같이 작성했다. </p>
<pre><code class="language-js">updateCount = (e, action) =&gt; {
    switch (action) {
      case &#39;add&#39;:
        this.setState({ count: this.state.count + 1 });
        break;
      case &#39;substract&#39;:
        const count = this.state.count - 1;
        this.setState({ count: count &gt; 1 ? count : 1 });
        break;
      default:
        break;
    }
  };</code></pre>
<p>코드를 살펴보면 <code>&quot;add&quot;</code> 와 <code>&quot;substract&quot;</code> 라는 <code>action</code> 을 받아서 더하거나 빼는 함수임을 알 수 있다. 두 개의 함수를 만들 것을 하나로 만들었기 때문에 작은 목표는 달성했지만 표현식의 특성을 이용하면 이렇게 바꿀 수 있었다. </p>
<pre><code class="language-js">updateCount = (e, action) =&gt; {
  const count =
    action === &#39;add&#39; ? this.state.count + 1 : this.state.count - 1;
  this.setState({ count: count &gt; 1 ? count : 1 });
  };</code></pre>
<p><code>count</code> 라는 변수에 삼항연산자의 결과를 바로 할당하고 이를 <code>setState()</code> 할 수 있도록 했다. </p>
<p>비록 복잡한 기능을 하는 함수도 아니고 간단한 이야기이지만 이런 작은 것들이 모여서 큰 것들을 만들 수 있다고 생각한다. 앞으로도 불필요한 코드를 줄여나가는 일에는 집중해 볼 예정이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 040 | 계산된 속성명]]></title>
            <link>https://velog.io/@jun_n3/TIL-040-%EA%B3%84%EC%82%B0%EB%90%9C-%EC%86%8D%EC%84%B1%EB%AA%85</link>
            <guid>https://velog.io/@jun_n3/TIL-040-%EA%B3%84%EC%82%B0%EB%90%9C-%EC%86%8D%EC%84%B1%EB%AA%85</guid>
            <pubDate>Sat, 11 Sep 2021 15:20:23 GMT</pubDate>
            <description><![CDATA[<h1 id="계산된-속성명">계산된 속성명</h1>
<blockquote>
<p>계산된 속성명이란 ES6 부터 지원되는 기능으로, 객체의 속성명이 최초에 결정되는 것이 아니라 동적으로 결정되는 것을 말한다. 이를 사용하는 경우에는 dot notation이 아니라 <code>[]</code> 을 사용한다. </p>
</blockquote>
<h2 id="사용하는-이유">사용하는 이유?</h2>
<p>계산된 속성명은 주로 비슷한 동작을 줄일 때 사용할 수 있다. 1차 프로젝트를 진행하면서 상품 리스트 페이지를 담당하게 되었는데 필터 기능, 페이지 사이즈 관련 기능, 페이지네이션 기능 등을 이용하려다 보니 비슷한 동작을 하는 여러 개의 함수가 만들어지게 됐다. </p>
<pre><code class="language-js">// 비슷한 3개의 함수
getViewType = type =&gt; {
    this.setState({ viewType: type });
  };

getFilterType = type =&gt; {
    this.setState({ filter: type });
  };

getCurrentPage = num =&gt; {
    this.setState({ page: Number(num) });</code></pre>
<p>기능에는 문제가 없었으나 계산된 속성명을 이용하면 이를 하나로 줄일 수 있어보였다. 3개의 함수 모두 단순하게 <code>setState()</code> 만을 목적으로 하고 있기 때문이었다. </p>
<h2 id="사용-예시">사용 예시</h2>
<pre><code class="language-js"> getPageOption = (value, setTarget) =&gt; {
    this.setState({ [setTarget]: value });
  };</code></pre>
<p>비슷한 동작을 하는 3개의 함수는 계산된 속성명을 통해 하나의 함수로 줄일 수 있었다. 위를 보면 <code>setTarget</code> 이라는 매개변수를 통해서 <code>setState</code> 안에서 <code>[setTarget]</code> 이 동적으로 계산되면서 상황에 맞게 들어가고 있음을 알 수 있다. 여기서 <code>[]</code>을 사용하지 않으면 <code>setTarget</code> 에 들어오는 인자가 아닌 <code>setTarget</code> 이라는 이름을 가진 프로퍼티에 <code>value</code> 가 할당되기 때문에 원하는 대로 동작하지 않는다. </p>
<p>개인적으로 프로젝트를 하면서 마음에 들었던 코드 중에 하나이다. 항상 느끼는 바이지만 동일한 동작을 하는 코드를 줄여나가고 가독성을 늘릴 수 있을 때 짜릿함이 느껴진다. ✨</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 039 | offset을 이용한 페이지네이션]]></title>
            <link>https://velog.io/@jun_n3/TIL-039-offset%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98</link>
            <guid>https://velog.io/@jun_n3/TIL-039-offset%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98</guid>
            <pubDate>Sun, 05 Sep 2021 13:08:59 GMT</pubDate>
            <description><![CDATA[<h1 id="페이지네이션">페이지네이션</h1>
<p>백엔드에서 보통 가지고 있는 데이터의 경우에는 그 양이 많은 경우가 대부분이다. 이런 데이터를 전부 가져와서 보여준다면 시간이 오래 걸리게 된다. 그렇기 때문에 한 번에 받을 데이터를 설정하여야 하는데, 이때 필요한 것이 바로 <code>Pagination</code> 이다.</p>
<h2 id="limit--offset">limit &amp; offset</h2>
<p>백엔드 입장에서는 보통 프론트엔드로부터 보내진 <code>limit</code> 을 통해서 몇 개의 데이터를 보내줄지 결정하게 되는데 이때 <code>offset</code> 에 대한 정보를 추가로 보내서 보내줄 데이터의 시작 지점을 결정할 수 있다. </p>
<p>만약 <code>offset</code> 이 0이고 <code>limit</code> 이 10이라면 백엔드에서는 인덱스가 0부터 9인 상품을 보내게 될 것 이다. 이때 다음 상품을 받기 위해서 프론트엔드에서는 <code>offset</code>을 새롭게 수정하여 보내줘야 한다. </p>
<h2 id="offset-구하기">offset 구하기</h2>
<p><code>offset</code> 을 구하는 방법이야 다양하게 있겠지만 간단한 방법을 하나 제시하고자 한다. 그것은 바로 현재 보여주고 있는 페이지의 숫자와 한 페이지에서 보여주는 상품의 갯수를 이용하는 방법이다. </p>
<p>즉, <code>offset = (page-1) * limit</code> 이다. 만약 2페이지를 볼 예정이고 10개의 상품을 보여줄 예정이라면 전달해 줄 <code>offset</code> 은 10이 될 것 이다. 이 요청에 따라 백엔드에서는 10번 인덱스부터 19번 인덱스까지 10개의 상품을 보낼 것이다.</p>
<blockquote>
<p>여기서 제시한 페이지네이션이 정답은 아니다. 통신 환경에 따라서 여러 가지 방법이 있기 때문에 상황에 맞는 방법을 택해서 사용하면 된다. 페이지네이션의 경우에 자주 사용되기 때문에 대략적인 흐름을 이해하는 것이 좋다고 생각한다. </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 038 | 동적 클래스 사용]]></title>
            <link>https://velog.io/@jun_n3/TIL-038-%EB%8F%99%EC%A0%81-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%82%AC%EC%9A%A9</link>
            <guid>https://velog.io/@jun_n3/TIL-038-%EB%8F%99%EC%A0%81-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%82%AC%EC%9A%A9</guid>
            <pubDate>Sun, 05 Sep 2021 12:56:50 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>처음에 공부를 할 때 <code>HTML / CSS</code> 로만 동적인 효과를 만드는 것은 뭔가 꼼수를 쓰는 것 같은 기분이 들었던 적이 있다. 하지만, 공부를 하면서 느낀 것은 수많은 동적 효과들을 <code>JavaScript</code> 를 통해서만 구현하는 것보다 간단하게 <code>HTML/CSS</code> 를 통해서 구현하게 되면 좋다는 것을 알게 됐다. 유용하게 사용될 동적 클래스에 대해서 알아보자. </p>
</blockquote>
<h1 id="동적-클래스">동적 클래스</h1>
<h2 id="동적-클래스-1">동적 클래스?</h2>
<p>동적인 클래스를 이용한다는 것은 조건에 따라서 <code>HTML</code> 태그의 <code>class</code> 를 변경하여 동적인 변화를 준다는 뜻이다. </p>
<p><img src="https://images.velog.io/images/jun_n3/post/eb19f967-7f90-4610-a63c-ed9e6aa1e55e/Sep-05-2021%2021-45-17.gif" alt=""></p>
<p>위의 예시처럼 <code>class</code> 의 변화를 통해서 동적인 효과가 부여되었다. </p>
<h2 id="예시">예시</h2>
<pre><code class="language-js"> &lt;div className=&quot;controller&quot;&gt;
          {BTN_TYPE.map((item, idx) =&gt; (
            &lt;button
              className={`${item.type} ${
                item.type === viewType ? &#39;active&#39; : &#39;&#39;
              }`}
              onClick={this.getViewType}
              id={item.type}
              key={idx}
            &gt;
              &lt;i className={item.icon} /&gt;
            &lt;/button&gt;
          ))}
        &lt;/div&gt;</code></pre>
<p>위의 예시를 보자. 위는 4가지 레이아웃 모드를 제공하는 버튼들을 구현하기 위해서 <code>Array.map()</code> 을 이용한 상황이다. 위에서 각 버튼에 부여되는 <code>className</code> 을 보면 조건에 따라서 각 아이템의 타입과 현재 보여질 타입이 같은 경우에 <code>active</code> 라는 클래스를 더해주는 것을 알 수 있다. 이를 통해서 버튼의 모습이 변경되도록 할 수 있다. </p>
<p><img src="https://images.velog.io/images/jun_n3/post/487af008-637a-4f2a-8457-179a300f1abe/Sep-05-2021%2021-54-03.gif" alt=""></p>
<p>버튼을 예시로 들었지만 버튼과 함께 변화되는 각 상품 이미지의 변화 역시 동적인 클래스 네임을 이용해서 구현하고 있다. 이처럼 다양한 상황에서 사용될 수 있기 때문에 기억하고 익숙해지면 좋을 것 같다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 037 | componentDidUpdate]]></title>
            <link>https://velog.io/@jun_n3/TIL-037-componentDidUpdate</link>
            <guid>https://velog.io/@jun_n3/TIL-037-componentDidUpdate</guid>
            <pubDate>Sun, 05 Sep 2021 12:37:01 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>1차 프로젝트를 진행하면서 <code>fetch</code> 를 사용해야하는 일이 많아졌다. 상품 리스트 페이지를 맡게 됐는데 라인프렌즈의 경우에 필터 관련된 기능들이 여러가지가 있어서 이 때 새 요청을 보내 다른 데이터를 받아오기 위해 <code>update</code> 와 관련된 라이프 사이클 메소드가 필요했다. </p>
</blockquote>
<h1 id="componentdidupdate">componentDidUpdate</h1>
<h2 id="기본-사용법">기본 사용법</h2>
<p><code>componentDidUpdate</code> 의 경우에는 <code>componentDidMount</code> 의 경우와는 다르게 <code>state</code> 나 부모 컴포넌트로부터 받은 <code>props</code> 에 변동사항이 있는 경우에 사용할 수 있는 요소이다. </p>
<p>사용법은 아래와 같다. </p>
<pre><code class="language-js">componentDidUpdate = (prevProps, prevState) =&gt; {
    fetch(&#39;data/ProductData.json&#39;)
      .then(result =&gt; result.json())
      .then(list =&gt; this.setState({ list, totalProducts: list.length }));
  };</code></pre>
<p>만약 위와 같은 상태로 사용을 하게 되면 어마어마한 일이 발생하게 된다. <code>fetch</code> 안에서 <code>setState()</code> 를 하고 있기 때문에 <code>state</code> 에 계속 변경이 생기게 되고 무수한 요청을 보내게 된다. 그렇기 때문에 이를 막기 위해서 <code>componentDidUpdate</code> 를 사용하는 경우에는 조건을 제시해줘야 한다. </p>
<h2 id="조건">조건</h2>
<pre><code class="language-js">componentDidUpdate = (prevProps, prevState) =&gt; {
    if (this.state.filter !== prevState.filter) {
      this.getData();
    }
  };</code></pre>
<p>위의 예시를 보면 현재 <code>state</code> 에 있는 <code>filter</code> 의 값과 이전 <code>state</code> 의 <code>filter</code> 의 값이 같지 않은 경우에만 <code>componentDidUpdate</code>가 발생할 수 있도록 했다. 이렇게 작성을 하면 원하는 조건에서만 데이터를 요청할 수 있다. </p>
<blockquote>
<p>라이프 사이클의 경우에 다룰 내용이 많이 있기 때문에, 나중에 한 번 제대로 정리를 해볼 생각이다. 우리가 원하는 시점에서 원하는 동작을 할 수 있는 라이프 사이클 메소드에 대해서 차근 차근 친해져보자. </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 036 | 조건부 렌더링]]></title>
            <link>https://velog.io/@jun_n3/TIL-036-%EC%A1%B0%EA%B1%B4%EB%B6%80-%EB%A0%8C%EB%8D%94%EB%A7%81</link>
            <guid>https://velog.io/@jun_n3/TIL-036-%EC%A1%B0%EA%B1%B4%EB%B6%80-%EB%A0%8C%EB%8D%94%EB%A7%81</guid>
            <pubDate>Mon, 30 Aug 2021 00:22:06 GMT</pubDate>
            <description><![CDATA[<h1 id="조건부-렌더링">조건부 렌더링</h1>
<p><code>React</code> 를 통해서 인스타그램 클론 코딩을 진행하면서 여러 일들이 있었지만 그 중에서 어려웠던 한 가지를 뽑자면 바로 <code>Lifecycle</code> 에 관련된 일이다. 정확하게 <code>Lifecycle</code>에 대해서 모르는 상태로 관련 메소드를 사용하다 보니 여러 에러를 겪을 수 밖에 없었다. </p>
<h2 id="react가-화면을-그리는-방식">React가 화면을 그리는 방식</h2>
<p><code>React</code>에서 특히 클래스형 컴포넌트를 사용하는 경우에 화면이 그려지는 방식은 아래와 같다. </p>
<p><img src="https://images.velog.io/images/jun_n3/post/4997f08b-696f-4175-b5cb-4d1527393da8/%E1%84%89%E1%85%A2%E1%86%BC%E1%84%86%E1%85%A7%E1%86%BC%E1%84%8C%E1%85%AE%E1%84%80%E1%85%B5.jpeg" alt=""></p>
<ol>
<li>생성자 함수인 <code>constructor()</code> 가 호출된다.</li>
<li>화면을 렌더링 하는 <code>render()</code> 메소드가 호출된다.</li>
<li><code>componentDidMount()</code> 가 호출된다.</li>
</ol>
<p>만약 내가 그리고자 하는(특히, <code>Array.map()</code> 을 이용해서 그릴 예정인) 데이터가 <code>componentDidMount()</code> 를 통해서 불러와야 하는 경우에는 아래와 같은 에러를 겪게 될 것이다.</p>
<p><img src="https://images.velog.io/images/jun_n3/post/b40d3b9d-d616-49f6-961f-02c07f132f2d/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-08-30%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%209.03.48.png" alt=""></p>
<p>나는 위의 상황을 정확히 알지 못했기 때문에 어째서 <code>Array.map()</code> 가 작동할 수 없는지에 대해서 많은 고민을 했다. </p>
<h2 id="componentwillmount">componentWillMount</h2>
<p>공식문서를 통해서 화면을 렌더링 할때 호출되는 순서에 따른 에러임을 알게 된 나는 <code>componentWillMount()</code> 를 사용했다. <code>render()</code> 보다 빠르게 호출되기를 원했기 때문이었다. 동작은 잘 되었지만 이는 옳은 방법이 아니었다. </p>
<p>리액트 공식문서에 따르면 <code>componentWillMount</code> 의 경우에 이름이 변경되기도 했으며, 추후 버전에서는 삭제될 예정이라고 한다. 이제는 <code>getDerivedStateFromProps()</code> 라는 메소드를 통해서 <code>render()</code> 전에 호출되도록 할 수 있지만 이는 매우 특별한 경우에만 사용하라고 되어있다. </p>
<h2 id="constructor">constructor()</h2>
<p>그렇다면, 대안이 무엇일까. 이 역시 문서에 따르면 <code>constructor()</code> 또는 <code>componentDidMount()</code> 를 통해서 해결하는 방법을 권장하고 있다. 여기서 <code>constructor()</code> 의 경우에는 생성자 함수이기 때문에 되도록 초기화와 관련된 내용만 처리하는 것을 권장하고 싶다. </p>
<h2 id="조건부-렌더링-1">조건부 렌더링</h2>
<p>마지막으로 우리에게는 결국 되돌아와서 <code>componentDidMount()</code> 라는 선택지가 주어졌다. 이를 쓰기 위해서 우리가 해줄 수 있는 것이 바로 <strong>조건부 렌더링</strong> 이다. </p>
<p>웹페이지를 구성하다보면 특정 상황이나 조건에 따라서 화면을 렌더링해야하는 경우가 생겨날 수 밖에 없는데 이때 사용하는 것이 조건부 렌더링이다. </p>
<pre><code class="language-js">// &amp;&amp; 연산자 사용
&lt;ul&gt;
    {comments &amp;&amp; comments.map(comment =&gt; (
      &lt;Comment
        key={comment.id}
        comment={comment}
        onDelete={this.handleDelete}
        onLike={this.handleLike}
      /&gt;
    ))}
&lt;/ul&gt;</code></pre>
<p>위의 코드를 보면 <code>comments</code> 가 <code>true</code> 라면 <code>comments.map()</code> 이 실행되도록 하고 있다. 위의 코드를 <code>삼항연산자</code> 를 통해서 작성할 수 있는데 단순히 <code>false</code> 경우에는 동작하지 않도록 하고 싶은 경우라면 <code>&amp;&amp;</code> 연산자가 가독성 측면에서 좋다. </p>
<p>위의 코드는 아래처럼 사용할 수도 있다. </p>
<pre><code class="language-js">// ?. 사용(optional chaining)
&lt;ul&gt;
    {comments?.map(comment =&gt; (
      &lt;Comment
        key={comment.id}
        comment={comment}
        onDelete={this.handleDelete}
        onLike={this.handleLike}
      /&gt;
    ))}
&lt;/ul&gt;</code></pre>
<p>위는 <code>?.(optional chaining)</code> 을 사용한 방식이다. 프로퍼티에 접근할 때 만약 <code>null</code> 이나 <code>undefined</code> 라면 단락하고 <code>undefined</code> 를 반환하는 방식이다. 남용하는 것은 유지보수에 좋지 않기 때문에 꼭 필요한 경우가 아니라면 지양하자. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[REACT | [westagram] 인스타그램 클론 프로젝트 후기]]></title>
            <link>https://velog.io/@jun_n3/REACT-westagram-%EC%9D%B8%EC%8A%A4%ED%83%80%EA%B7%B8%EB%9E%A8-%ED%81%B4%EB%A1%A0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@jun_n3/REACT-westagram-%EC%9D%B8%EC%8A%A4%ED%83%80%EA%B7%B8%EB%9E%A8-%ED%81%B4%EB%A1%A0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Sun, 29 Aug 2021 08:55:16 GMT</pubDate>
            <description><![CDATA[<h1 id="📋-project-overview">📋 Project Overview</h1>
<h2 id="span-stylecolor0066ff첫-react-프로젝트span"><span style="color:#0066ff">첫 React 프로젝트</span></h2>
<p><code>JavaScript</code> 를 통해서 구현했던 인스타그램을 다시 <code>React</code> 를 통해서 구현하는 프로젝트를 하게 됐다. 기존에 <code>DOM</code> 을 조작해서 넣었던 기능들을 <code>state</code> 와 <code>props</code> 를 이용해서 구현하려다 보니 어려운 점도 많았는데, 돌이켜보면 어째서 <code>React</code> 를 써야하는지 느끼게 된 프로젝트이기도 했다. 초면이었던 <code>React</code> 와 조금은 친해지게 된 <code>위스타그램</code> 에 대해 정리해본다.</p>
<h2 id="span-stylecolor0066ff제작-기간span"><span style="color:#0066ff">제작 기간</span></h2>
<p><strong>2021.08.23 ~ 2021.08.26</strong></p>
<h2 id="span-stylecolor0066ff사용-기술span"><span style="color:#0066ff">사용 기술</span></h2>
<p><strong>- HTML / SASS</strong>
<strong>- JavaScript(ES6 + )</strong>
<strong>- React</strong></p>
<h2 id="span-stylecolor0066ff구현-사항span"><span style="color:#0066ff">구현 사항</span></h2>
<ul>
<li>*<em><code>Login</code> : 아이디, 패스워드 유효성 검사 로직 *</em></li>
<li>*<em><code>Login</code> : 백엔드 API를 통한 회원가입 &amp; 로그인 구현 *</em></li>
<li>*<em><code>Main</code> : Mock data 통한 피드 및 댓글 구현 *</em></li>
<li>*<em><code>Main</code> : 댓글 추가 &amp; 삭제 구현 *</em></li>
<li>*<em><code>Nav</code> : 검색어 자동완성 기능 구현 *</em></li>
<li>*<em><code>Nav</code> : 프로필 메뉴 토글 기능 구현 *</em></li>
</ul>
<h2 id="span-stylecolor0066ff결과-화면span"><span style="color:#0066ff">결과 화면</span></h2>
<p>** 댓글 추가 기능 **</p>
<p><img src="https://images.velog.io/images/jun_n3/post/00b450aa-ec3d-4baf-a339-cbabfeb164ca/Aug-29-2021%2016-14-38.gif" alt=""></p>
<p>** 댓글 삭제 &amp; 좋아요 기능 **
<img src="https://images.velog.io/images/jun_n3/post/9a57f5bb-5fe0-4ade-a5e2-b28fc16a8fb7/Aug-29-2021%2016-14-47.gif" alt=""></p>
<p>** 아이디 검색 기능 **
<img src="https://images.velog.io/images/jun_n3/post/11383d53-92ee-4ad1-a551-11ae9eac30c7/Aug-29-2021%2016-26-28.gif" alt=""></p>
<p>** 프로필 메뉴 토글 기능 **</p>
<p><img src="https://images.velog.io/images/jun_n3/post/b17b9f58-48e3-4195-8783-a34d4fc5b8c9/Aug-29-2021%2016-28-46.gif" alt=""></p>
<h1 id="👀-project-review">👀 Project Review</h1>
<h2 id="span-stylecolor0066ffreact-님은-뭘까span"><span style="color:#0066ff">React... 님은 뭘까?</span></h2>
<ul>
<li><code>UI</code> 를 위한 자바스크립트 라이브러리</li>
<li><code>선언적</code> 특징을 가지고 있는 라이브러리</li>
<li>재사용성이 가능한 <code>UI</code> 단위 <code>컴포넌트</code></li>
</ul>
<p><code>리액트</code> 관련 세션을 듣고 생각보다 익숙한 모습에 친근하게 느껴졌다. <code>JSX</code> 는 <code>HTML</code> 태그와 모습이 비슷했고 <code>class</code> 가 낯선 것만 빼면 <code>JavaScript</code> 니까 금방 할 수 있을거라 굳게 믿었다. </p>
<p>하지만... 세션 후에 <code>JS</code> 로 구했했던 코드들을 <code>CRA</code> 폴더에 옮기고 화면을 그리자 당황스러움이 느껴졌다. 심지어, 첫 협업으로 같은 폴더를 공유하며 진행해야하다보니 함부로 파일들을 수정할 수 없었는데 이로 인해서 많은 어려움이 있었다. </p>
<p>특히 <code>Nav</code> 부분을 맡아서 진행하다가 나의 허술함을 강력 어필하게 됐다. <code>master</code> 가 아닌 개인 <code>branch</code> 에서 <code>nav</code> 관련 <code>branch</code> 를 생성했다가 작업물이 사라지는 경험을 하기도 하고 여러 번의 수정으로 팀원들에게 반복되는 사과를 하기도 했다. </p>
<p>무엇보다... <code>state</code> 와 <code>props</code> 를 사용해서 기능들을 구현하는 방법은 사고의 방향이 다르게 느껴져서 혼란스러웠다. 하지만, 결국에는 <code>React</code> 가 얼마나 좋은 것인지 알게 되었다. </p>
<h2 id="span-stylecolor0066ff리액트와-강강수월래-🤟🏻-span"><span style="color:#0066ff">리액트와 강강수월래! 🤟🏻 </span></h2>
<h3 id="api를-통한-통신">API를 통한 통신!</h3>
<p><img src="https://images.velog.io/images/jun_n3/post/a6617609-2a6a-4042-b684-dec9d10b2875/Aug-25-2021%2019-01-59.gif" alt=""></p>
<p>이번 주 세션 중에서 가장 재밌었던 순간을 뽑자면 바로 백엔드와 연결을 해보는 시간이 아니었나 싶다. </p>
<pre><code class="language-js">// 회원가입 코드
handleSignUp = event =&gt; {
    fetch(&#39;${baseUrl}/users/sign-up&#39;, {
      method: &#39;POST&#39;,
      body: JSON.stringify({
        name: &#39;하루&#39;,
        email: this.state.id,
        password: this.state.password,
        phone_number: &#39;010-1111-2222&#39;,
        date_of_birth: &#39;2001-01-02&#39;,
      }),
    })
      .then(result =&gt; result.json())
      .then(result =&gt; console.log(result));
  };</code></pre>
<p>프론트엔드의 경우에는 간단한 유효성 검사 정도의 로직만 구현을 했다보니 세션 후에 실습 시간에 바로 코드를 작성해야 했다. 백엔드 페어로 지정되신 우주님에게 <code>key</code> 값과 <code>엔드포인트</code> 에 대해서 물은 후에 바로 코드를 작성했다. 다행스럽게도 금방 <strong>400 에러</strong> 를 만날 수 있었고 연결은 됐다!! 하며 즐거워했다. </p>
<p>이 과정에서 <strong>400 에러</strong> 를 자주 봤었는데 이유는 우주님이 작성하신 유효성 검사에 대해서 정확하게 묻지 않고 진행하다보니 생긴 일이었다. 어떤 값들을 입력해야하는지 정확히 의견을 나누고 진행하니 곧 <code>CREATE</code> 라는 메세지를 받을 수 있었다. 나도 모르게 환호성을 질렀던 기억이 난다. <del>백엔드 최고!</del> </p>
<pre><code class="language-js">// 로그인 기능
 handleLogin = event =&gt; {
    fetch(&#39;${baseUrl}/users/login&#39;, {
      method: &#39;POST&#39;,
      body: JSON.stringify({
        email: this.state.id,
        password: this.state.password,
      }),
    })
      .then(result =&gt; result.json())
      .then(result =&gt; {
        result.TOKEN
          ? this.props.history.push(&#39;/main-cheoljin&#39;)
          : alert(&#39;아이디와 비밀번호를 맞게 작성해주세요&#39;);
      });
  };</code></pre>
<p>이제 회원가입 한 아이디와 비밀번호를 입력해서 로그인만 시키면 됐다. 엔드 포인트 주소를 바꿔서 입력해 준 후에 <code>body</code> 에 관련된 내용을 담아 보냈더니 진짜로 토큰을 받아볼 수 있었다. 세션으로 듣기만 했던 토큰을 실제로 확인하니 매우 신기했다.</p>
<p><img src="https://images.velog.io/images/jun_n3/post/d648cc20-66f8-4cd3-9294-0264068a3e71/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-08-29%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.07.49.png" alt=""></p>
<p>알아먹을 수 없는 토큰이라는 존재가 이렇게 기쁜 존재인지 몰랐다. </p>
<h3 id="이게-컴포넌트">이게 컴포넌트?</h3>
<p>리액트를 진행하면서 가장 좋았던 점은 한 눈에 보기가 불편했던 코드가 <code>컴포넌트</code> 를 나눌 수록 매우 보기 편해진다는 점이었다. </p>
<pre><code class="language-js">import React, { Component } from &#39;react&#39;;
import Nav from &#39;../../../components/Nav/Nav&#39;;
import Aside from &#39;./Aside/Aside&#39;;
import Feed from &#39;./Feed/Feed&#39;;
import &#39;./Main.scss&#39;;

class MainCheoljin extends Component {
  state = {
    feeds: [],
  };

  render() {
    return (
      &lt;div className=&quot;main-cheoljin&quot;&gt;
        &lt;Nav /&gt;
        &lt;main className=&quot;main&quot;&gt;
          &lt;div className=&quot;main__container&quot;&gt;
            &lt;section className=&quot;main__feeds&quot;&gt;
              {this.state.feeds.map(feed =&gt; (
                &lt;Feed key={feed.id} feed={feed} comments={feed.comments} /&gt;
              ))}
            &lt;/section&gt;
            &lt;Aside /&gt;
          &lt;/div&gt;
        &lt;/main&gt;
      &lt;/div&gt;
    );
  }
}

export default MainCheoljin;</code></pre>
<p>기존에는 매우 길어서 원하는 부분을 찾기 위해서 <code>cmd</code> + <code>F</code> 가 필수였는데 지금은 한 눈에 어떤 구조로 만들어져 있는지 볼 수 있게 되었다. 처음에는 기존의 코드를 <code>컴포넌트</code> 로 바꾼다는 점이 어렵게 느껴졌는데 시간이 갈 수록 <code>컴포넌트</code> 들을 더 나누면서 즐거웠다. </p>
<h3 id="하드코딩-멈춰-🖐🏻">하드코딩 멈춰! 🖐🏻</h3>
<p><img src="https://images.velog.io/images/jun_n3/post/16bcaa07-9b5b-4281-8983-219066402d2c/giphy%20(3).gif" alt=""></p>
<p><code>리액트</code>를 배우고 적용하면서 정말 기뻤던 부분은 바로 <code>Array.map()</code> 와 <code>컴포넌트</code> 가 시너지를 만들어 하드코딩을 정말 많이 줄일 수 있게 되었다는 점이었다. 이전에는 피드를 여러개 만들고 싶거나 댓글을 여러개 만들고 싶은 경우에 하나씩 하드코딩하면서 만들어야하는 불편함이 있었다. 이는 코드 역시 쓸데없이 길게 만들어서 가독성도 많이 헤치는 점이었다. </p>
<pre><code class="language-js">&lt;section className=&quot;main__feeds&quot;&gt;
 {this.state.feeds.map(feed =&gt; (
  &lt;Feed key={feed.id} feed={feed} comments={feed.comments} /&gt;
  ))}
&lt;/section&gt;</code></pre>
<p>이렇게 <code>컴포넌트</code> 를 만들고 <code>Array.map()</code> 을 이용하면 짧은 코드로도 여러 개의 피드나 댓글을 만들 수 있다. 또한, <code>JavaScript</code> 를 이용해서 <code>DOM</code> 에 이벤트 리스너를 등록하고 함수들을 추가했을 때는 한 개의 피드에서만 정상적으로 동작하는 단점이 있었는데 리액트에서는 <code>컴포넌트</code> 를 통해서 자연스럽게 모든 피드에서 동작하도록 할 수 있었다. </p>
<h3 id="여전히-어렵지만">여전히 어렵지만...!!</h3>
<p><code>리액트</code> 의 장점에 푹 빠진 것 같기도 하지만, 여전히 어려운 점은 많다. <code>main</code> 에서 모든 <code>state</code> 를 관리하면서 댓글 추가 &amp; 삭제 기능의 로직이 복잡한 것을 바꿔보기 위해서 <code>feed</code> 에서 자신의 관련된 상태만 받아서 이용할 수 있도록 변경하였는데 이 과정에서  여러 오류를 겪었다. 컴포넌트가 렌더링 되는 과정에 대해서 정확한 이해가 부족하다 보니 생긴 일이었다. </p>
<p><code>constructor</code> -&gt; <code>render()</code> -&gt; <code>componentDidMount()</code> 순으로 발생한다는 점은 겪어보면서 느낄 수 있던 점이었다. 그 과정에서 <code>Optional Chaining</code> 이나 <code>componentWillMount</code> 등에 대해서도 공부해 볼 수 있었다. 리액트에 대해서 공부할 점이 앞으로도 정말 많을 것 같다는 생각이 든다.</p>
<p>역으로 검색 기능 같은 경우에는 복잡했던 코드가 간단해지기도 했다. </p>
<pre><code class="language-js">const searchResult = searchValue
      ? searchList.filter(list =&gt; list.id.indexOf(searchValue) === 0)
      : PREVIEW_DATA;</code></pre>
<p>만약 사용자가 검색어를 입력한다면 그거에 맞는 데이터만을 전달하여 <code>SearchItem</code> 을 <code>Array.map()</code> 을 통하여 컴포넌트를 그려낼 수 있도록 하고 아니라면 <code>PREVIEW_DATA(이전 검색)</code> 을 보여주도록 했다. 전에 <code>DOM</code> 을 통해서 구현했던 것에 비해 훨씬 간단하게 구현할 수 있어서 놀라웠다. </p>
<h2 id="span-stylecolor0066ff-프로젝트-회고-span"><span style="color:#0066ff"> 프로젝트 회고 </span></h2>
<h3 id="칭찬하고-싶은-점">칭찬하고 싶은 점</h3>
<p>우선, 여러가지 일들이 많았던 기간 동안 좌절하지 않고 끈기 있게 리액트에게 말을 걸었던 나에게 칭찬을 해주고 싶다. 쉽지도 않았고 정말 다사다난했지만 새로운 기능에 두려움보다는 호기심으로 다가가기 위해서 노력했다. </p>
<p>무엇보다, 여러 동기분들의 코드를 보면서 문제들이 생겼을 때 해결해보기 위해서 고민했던 시간들이 있어서 좋았다고 생각한다. 다른 사람의 코드를 보는 연습, 문제가 생겼을 때 천천히 디버깅 하는 연습이 되었다고 생각한다. 다소 많은 <code>console.log()</code> 를 찍으며 귀찮게 해드리지는 않았나 싶기는 하지만 같이 문제를 해결해나가는 즐거운 시간이었다고 믿는다. </p>
<h3 id="개선하고-싶은-점">개선하고 싶은 점</h3>
<p>여전히, 코드를 정리해나가고 불필요한 부분을 줄이는 연습은 부족하다고 생각이 든다. 또, 공식문서를 더 많이 읽으면서 항상 최선인지에 대해서 고민하는 연습도 필요하다고 느낀다. 원하는 만큼 기능들을 구현해보기는 했지만 그 과정에서 불필요한 코드가 남기도 하고 다소 부적절한 방법으로 코드를 작성하기도 했다. 이런 부분들은 앞으로 계속 고쳐나가야 하는 점이라고 생각한다. </p>
<p>누가 봐도 읽기 편한 코드, 이해하기 좋은 코드를 작성하도록 노력하자.</p>
<hr>
<h1 id="✨-빛나는-동기들">✨ 빛나는 동기들</h1>
<p>같은 동기분들에게 가끔 질문을 받으면 무언가 도움이 될 수 있는 것 같아 기쁘다. 비슷한 문제를 겪었고 경험했기 때문에 적절한 도움을 드릴 수 있는 경우가 대부분이다. 나 또한, 어려운 문제가 있을 때 질문 하나로 해결이 되는 경우도 정말 많다. </p>
<p>혼자서 진행했다면 어렵고 힘들었을 이 시간들을 함께 걸어나갈 수 있는 동기들이 있어서 정말 좋다. 특히, 나의 열정 정도는 작아보이게 만들만큼 열심히 자신의 길을 걸어나가는 동기님들이 있어서 뒤쳐지지 않기 위해 한 발, 두 발 더 나갈 수 있다. </p>
<p>지금의 내가 옆에서 함께 일하고 싶은 개발자로서 성장하고 있는지는 아직 잘 모르겠다. 하지만, 꼭... 옆에서 함께하고 싶은 개발자가 되도록 더욱 단단하게 걸어보려고 한다. 이 시기를 함께 해주는 24기 동기분들 모두에게 정말 감사하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Wecode ✨ 한 달의 기록]]></title>
            <link>https://velog.io/@jun_n3/Wecode-%ED%95%9C-%EB%8B%AC%EC%9D%98-%EA%B8%B0%EB%A1%9D</link>
            <guid>https://velog.io/@jun_n3/Wecode-%ED%95%9C-%EB%8B%AC%EC%9D%98-%EA%B8%B0%EB%A1%9D</guid>
            <pubDate>Sun, 29 Aug 2021 03:52:34 GMT</pubDate>
            <description><![CDATA[<h1 id="🗒-위코드에서의-한-달">🗒 위코드에서의 한 달</h1>
<h2 id="📲-첫-백엔드와의-통신">📲 첫 백엔드와의 통신!!</h2>
<p>4주차가 되면서 처음으로 백엔드와 통신을 해볼 수 있는 기회가 생겼다. 바로 <strong>회원가입 - 로그인 실습</strong>이었다. 그 동안 프론트는 화면을 그리고 <code>Mock data</code> 를 이용한 실습정도만 진행을 해봤고 백엔드는 API를 만들기는 했지만 화면으로 볼 기회는 없었는데 드디어 해 볼 수 있었다. </p>
<p><img src="https://images.velog.io/images/jun_n3/post/5f70e060-85a0-4ad2-a197-dae60f3fe1f2/Aug-25-2021%2019-01-59.gif" alt=""></p>
<p>이 과정에서 얻은 깨달음도 있었는데, 그건 <code>key</code> 값의 경우에 많은 소통이 필요하다는 점이었다. 만약, 프론트엔드와 백엔드가 <code>key</code>에 대해 소통이 없다면 무수한 <strong>400 에러</strong> 를 만나게 될 것이다. <del>그 다음에 땀을 흘리며 잠시만요!? 를 외치겠지...</del></p>
<h2 id="👊-리액트">👊 리액트</h2>
<p>개발자가 되기를 마음 먹으면서, 특히 프론트엔드 개발자가 되기로 결심하면서 자주 들었던 라이브러리가 <code>리액트</code> 였다. 위코드에 가기 전에 경험을 해보고 싶었지만 <code>HTML/CSS</code> 와 <code>JavaScript</code> 에서 공부할 것이 많았고 <code>JavaScript</code> 역시 별로 공부 하지 못한 채로 위코드에 가게 됐다. </p>
<p>그렇기에 개인적인 목표로 프로젝트 전에 최대한 <code>리액트</code> 와 친해지고 싶다는 생각을 했다. 그래서 하나의 기능을 구현하면서도 정말 많은 고민을 해보려고 노력했던 것 같다. </p>
<p>특히, <code>Mock data</code> 실습을 진행하면서 기존의 <code>state</code> 구조가 변경되었고 댓글 추가와 삭제 기능이 망가지게 되었는데 이를 고치기 위해서 정말 여러 방법을 사용해봤다. 우선은 <code>main</code> 페이지에서 <code>state</code> 를 가지고 있었기에 깊이 탐색해 가면서 우리가 변경할 요소를 찾아야했고 이를 구현하기 위해서 고민을 많이 했다. 그 과정에서 <code>props</code> 를 통한 자식 컴포넌트와 부모 컴포넌트 관계에 대해서 많은 깨달음을 얻었던 것 같다. </p>
<p>이상한 비유지만 이해를 쉽게 하기 위해서 나는 기숙사 2층에서 몰래 치킨을 먹기 위해 줄이 달린 바구니를 내리는 비유를 많이 썼다. </p>
<ol>
<li><code>props</code> 라는 <code>바구니(함수)</code>를 통해서 <code>1층(자식 컴포넌트)</code>에 내려보낸 뒤 </li>
<li>먹고자 하는 <code>치킨(필요한 인자)</code> 을 받아서 </li>
<li><code>2층(부모 컴포넌트)</code> 에서 맛있게 <code>먹는(state 변경)</code> 것이다.</li>
</ol>
<p>또한, 라이프 사이클에 대해서 생각해보는 계기도 있었는데. 상태값의 일부를 피드에서 관리하도록 하기 위해서 <code>constructor()</code>, <code>componentDidMount</code> 등 다양한 방법을 시도했다. 이 때, 어떤 방법이 더 좋은 것인지 고민이 들어서 멘토님들에게 많이 물어보기도 했는데 다양한 의견을 들을 수 있어서 좋았다. </p>
<p><strong>이제는 안녕!</strong></p>
<p><img src="https://images.velog.io/images/jun_n3/post/cebd76e7-faeb-4b96-9a94-fe1a49c6deb3/Aug-29-2021%2012-16-25.gif" alt=""></p>
<p>자바스크립트로 인스타그램을 만들 때 검색 기능까지 구현을 했었다. <code>리액트</code> 로 전환을 하면서 검색 기능이나 프로필 메뉴 토글 등은 미션에 없어서 넘어갈까 했는데 <strong>현재</strong>님의 이야기를 듣고 개인적으로 진행을 해봤다. </p>
<p>생각했던 것보다 프로필 메뉴 토글 기능의 경우에는 <code>이벤트 버블링</code> 을 어떻게 구현해야 하는지 고민이 들어서 어려움이 있었고 검색 기능 같은 경우에는 오히려 조금 편하게 구현할 수 있었다. </p>
<hr>
<h1 id="✨-어떤-시간이었나">✨ 어떤 시간이었나</h1>
<h2 id="🪄-보물창고">🪄 보물창고</h2>
<p>위코드에서 한 달을 보내면서 정말 많은 것들을 얻을 수 있었다. 특히, 다른 분들의 코드를 구경하는 것을 좋아해서 많은 동기분들의 코드를 볼 수 있었는데 매번 느끼는 것이지만 배울 것들이 정말 많다. </p>
<p>무엇보다 배움에 대한 태도에 대해서 정말 많이 배울 수 있었다. 단순히 코드를 작성하고 기능을 구현하는 것에서 멈추지 않고 되돌아가 스텝을 되밟아보면서 성취를 더 큰 성취로 만드는 동기분을 보면서 느낀게 많았다. </p>
<p>작은 성취에 머물러 있지는 않은지, 여러가지를 해보고 싶다는 마음으로 중요한 것들을 놓치고 있지는 않은지. 이제 1차 프로젝트를 진행하게 되면서 더욱 바빠지고 많은 성취들을 얻게 될텐데 이 마음을 기억하며 꼭 복기하는 습관을 키워볼려고 한다. </p>
<h2 id="🏃🏻-결국엔-똑같다">🏃🏻 결국엔 똑같다</h2>
<p>한 달을 마무리하면서 연우님이 해주신 말이 있다. 지금 가장 빠른 사람이나 가장 느린 사람이나 나중에는 비슷하다는 말이다. 나는 이 말이 공감됐다. 개발자의 길을 걷기로 한 이상, 그 마음이 진심이었다면. 우리는 앞으로도 많은 어려움을 겪으며 성장할 것이다. 특히, 개발자라고 당당하게 외치기는 살짝 부끄러운 지금의 성취는 앞으로 얻어야할 많은 성취들에 비하면 작다. <strong>한 달간의 우리의 성장이 작아서가 아니라 우리가 앞으로 할 수 있는 것들이 많아서 그렇다.</strong> </p>
<p>물론, 가만히 자기 자리에 있는다고 똑같아 지지는 않는다. 그저, 진심이면 된다. 개발자가 되겠다는 마음, 나아가 선한 영향력을 개발로서 이루고 싶다는 마음이면 그렇게 될 것이라고 믿는다. 배워나가는 지금의 우리에게 실력은 다른 것이 아니라 진심이고 열정이다. </p>
<h2 id="🔥-이제-프로젝트">🔥 이제 프로젝트!</h2>
<p>이제 조금은 두려운 프로젝트를 앞두고 있다. 여전히 작고 작은 내가 민폐를 주지는 않을까 고민도 되고 새로운 사람들과 의사소통하는 일이 걱정되기도 하지만 그 동안의 한 달을 보냈던 것처럼 진심으로 하루 하루를 보내려고 한다.</p>
<p>여러 후기나 멘토님들의 말을 들어보면 프로젝트는 결국 각자의 능력으로 미션들을 쳐나가는 것이 아니라 함께 소통하면서 이뤄나가는 것이라는 생각이 든다. 팀 속에서 어떤 태도로 임할 때 더 큰 시너지를 만들어 낼 수 있을 지 고민해야겠다. </p>
<hr>
<p>**
한 달이라는 시간이 흐르면서 많은 인풋을 소화하느라 지치기도 하는 시간이었던 것 같아요. 커피 한 잔으로 힘이 난다면, 얼마든지 드리고 싶어요 👏! 이제 시작하는 프로젝트도 함께 화이팅입니다. 💪 
**</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 035 | React의 state와 props]]></title>
            <link>https://velog.io/@jun_n3/TIL-035-React%EC%9D%98-state%EC%99%80-props</link>
            <guid>https://velog.io/@jun_n3/TIL-035-React%EC%9D%98-state%EC%99%80-props</guid>
            <pubDate>Thu, 26 Aug 2021 14:08:22 GMT</pubDate>
            <description><![CDATA[<h1 id="📲-state--props">📲 state &amp; props</h1>
<p>리액트를 처음 접하면 당황스러운 일이, 바로 <code>state</code> 와 <code>props</code> 이다. 리액트는 선언적 특징을 가지고 있기 때문에 직접 <code>DOM</code> 요소를 조작하지 않고 <code>state</code> 의 변화를 통해서 <code>UI</code> 를 리-렌더링한다. 그렇기 때문에 이전에 명령을 통해서 조작하던 방식과는 사고의 흐름이 다르다고 느껴졌다. 하지만, <code>state</code>와 <code>props</code> 은 알면 알수록 재밌고 흥미로운 요소이다. 이에 대해서 정리해보려고 한다. </p>
<h2 id="state">state</h2>
<p><code>state</code> 는 컴포넌트가 가지고 있는 상태값을 의미한다. 더 정확히는 <code>UI</code>에 대한 정보를 담고 있는 객체라고 볼 수 있다. <code>state</code> 는 불변값이 아니며 언제든지 사용하고 변경될 수 있다. </p>
<pre><code class="language-js">class Main extends Component {
  state = {
    feeds: [],
  };

  render() {
    return (
      &lt;main className=&quot;main&gt;
        &lt;Nav /&gt;
            &lt;section className=&quot;main__feeds&quot;&gt;
              {this.state.feeds.map(feed =&gt; (
                &lt;Feed key={feed.id} feed={feed} comments={feed.comments} /&gt;
              ))}
            &lt;/section&gt;
            &lt;Aside /&gt;
        &lt;/main&gt;
    );
  }
}

export default Main;</code></pre>
<p>위의 예시는 메인 페이지의 컴포넌트에서 <code>state</code> 를 가지고 있는 모습이다. 메인 페이지는 <code>state</code>에 <code>feeds</code> 라는 배열을 가지고 있다. 이 배열 안에는 또 각 피드의 정보를 담고 있는 객체들이 들어갈 예정이다. 지금은 생성하면서 초기화만 진행했기 때문에 <code>feeds</code> 에 빈 배열이 들어와 있다. </p>
<pre><code class="language-js">// mock data 받아오기
componentDidMount = () =&gt; {
    fetch(&#39;http://localhost:3000/data/CommentDataCJ.json&#39;, {
      method: &#39;GET&#39;,
    })
      .then(result =&gt; result.json())
      .then(feeds =&gt; {
        this.setState({
          feeds,
        });
      });
  };</code></pre>
<p>위의 코드는 <code>componentDidMount()</code> 를 통해서 <code>mock data</code> 를 받아오고 이를 이용해서 <code>setState</code> 를 진행했다. 여기서 <code>setState</code> 를 사용해서 업데이트를 한 점을 보자. <code>state</code> 는 <code>UI</code>에 대한 정보를 가지고 있고 이를 렌더링 하기 위해서 존재한다고 볼 수 있다. 만약, <code>state</code> 의 값을 직접 조작하는 경우에는 <code>state</code>의 변화는 있겠지만 <code>render()</code> 가 호출되지 않아서 리-렌더링이 생겨나지 않는다. 그렇기 때문에 <code>setState()</code> 를 호출하고 이 때, <code>render()</code> 가 호출되기 때문에 정상적으로 <code>UI</code>를 그릴 수 있게 된다. </p>
<h3 id="setstate의-비동기성">setState의 비동기성</h3>
<p><code>state</code> 를 사용하면서 겪었던 어려움은 <code>setState()</code> 가 비동기적으로 작동하는 점이었다. <code>state</code> 가 잘 업데이트 되는지 확인하기 위해서 <code>console.log(this.state)</code> 를 사용하는 과정에서 한 박자 느린 반응을 보게 된다거나 전혀 예상하지 못한 흐름으로 동작한다거나 하는 오류들을 겪었다. 특히, <code>ID</code> 와 <code>PASSWORD</code> 를 입력하는 상황에서 한 쪽이 사라지는 경우를 겪기도 했다.</p>
<pre><code class="language-js">this.setState({
  counter: this.state.counter + this.props.increment,
});</code></pre>
<p>만약 위와 같은 코드를 사용하게 된다면 카운터가 제대로 업데이트 되지 않는 상황을 겪을 수도 있다. 되도록 <code>this.state</code> 와 <code>this.props</code> 을 사용할 때 비동기적으로 업데이트 될 수 있음을 명심하고 사용하는 것이 좋다.</p>
<h2 id="props">props</h2>
<p><code>state</code> 가 컴포넌트 본인이 가지고 있는 상태값이라면 <code>props</code> 은 부모로부터 받은 속성 객체라고 볼 수 있다. 여기에는 배열, 객체 등이 들어갈 수 있고 메소드도 가능하다. <code>state</code> 는 본인이 가지고 있는 값이기 때문에 얼마든지 변경이 가능하지만 <code>props</code> 는 읽기 전용이기 때문에 자식 컴포넌트에서 수정할 수가 없다.  </p>
<h3 id="사용하기">사용하기</h3>
<pre><code class="language-js">class Comment extends Component {
  handleDelete = e =&gt; {
    this.props.onDelete(this.props.reply);
  };

  handleLike = e =&gt; {
    this.props.onLike(this.props.reply);
  };

  render() {
    const { userName, comment, isUser, isLike } = this.props.reply;
    return (
      &lt;li className=&quot;comment-cheoljin&quot;&gt;
        &lt;span&gt;{userName}&lt;/span&gt;
        &lt;span&gt;{comment}&lt;/span&gt;
        &lt;button
          type=&quot;button&quot;
          className=&quot;comment__heart&quot;
          onClick={this.handleLike}
        &gt;
          &lt;i className={isLike ? &#39;fas fa-heart&#39; : &#39;far fa-heart&#39;} /&gt;
        &lt;/button&gt;
        &lt;button
          type=&quot;button&quot;
          className=&quot;comment__delete&quot;
          onClick={this.handleDelete}
        &gt;
          &lt;i
            className={isUser ? &#39;far fa-trash-alt&#39; : &#39;far fa-trash-alt none&#39;}
          /&gt;
        &lt;/button&gt;
      &lt;/li&gt;
    );
  }
}

export default Comment;
</code></pre>
<p>위의 예시를 보자. <code>props</code> 를 구조분해할당을 통해서 <code>userName</code>, <code>comment</code>, <code>isUser</code>, <code>isLike</code> 로 나눴다. 그 후에 각 필요한 부분에 <code>{}</code> 을 통해 사용했다. 또한, <code>handleDelete</code> 등의 함수를 보면 부모 컴포넌트로부터 전달 받은 <code>props</code> 객체의 메소드인 <code>onDelete</code>를 호출하는 함수 임을 알 수 있다. </p>
<p>이처럼 <code>props</code> 는 부모 컴포넌트와 자식 컴포넌트를 연결해주는 다리의 역할을 한다. </p>
<p>더 설명해보자면 자식 컴포넌트인 <code>Comment</code> 에 있는 <code>&lt;button&gt;</code> 에서 <code>onClick</code> 이벤트가 발생하게 되면 <code>handleDelete</code> 함수가 호출되고 <code>handleDelete</code> 함수는 <code>props</code>로 전달받은 <code>onDelete</code> 함수를 호출한다. 이때 인자로 <code>props</code> 인 <code>reply</code> 를 전달할 수 있다.</p>
<p>위와 같은 방식으로 자식 요소에서 발생한 이벤트를 부모 컴포넌트로 전달해서 <code>state</code> 를 업데이트 할 수 있다. </p>
<h1 id="✨-마치며">✨ 마치며</h1>
<p>오늘은 <code>state</code> 와 <code>props</code> 에 대해서 간단하게 정리했다. 리액트를 처음 접하게 되는 경우에는 어떻게 이용해야 좋을지 막막한 기분을 느끼는 경우가 많은데 사용하는 방법에 대해서 잘 이해한다면 바닐라 자바스크립트만을 사용하는 경우보다 훨씬 편함을 알 수 있다. </p>
<p>앞으로 평생 함께해야 할 <code>state</code>와 <code>props</code>. 막히는 부분이 있는 경우에는 꼭 리액트 공식 문서에서 답을 찾으며 계속 공부해 볼 예정이다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 034 | Mock 데이터와 map 이용하기]]></title>
            <link>https://velog.io/@jun_n3/TIL-033-Mock-%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%99%80-map-%EC%9D%B4%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jun_n3/TIL-033-Mock-%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%99%80-map-%EC%9D%B4%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 25 Aug 2021 15:02:31 GMT</pubDate>
            <description><![CDATA[<h1 id="🔥-mock--map">🔥 Mock &amp; map</h1>
<p>복사 &amp; 붙여넣기 ... 복사 &amp; 붙여넣기 ... 이 지겨운 반복행위를 어떻게 하면 줄일 수 있을까. 그리고 백엔드에서 아직 API를 만들지 못했다고 하는데... 화면이 잘 나오는지 어떻게 알 수 있을까? 또 하드코딩인가!? 이런 고민을 해결하기 위해서 좋은 방법이 바로 <code>Mock</code> 데이터와 <code>map</code> 을 이용하는 방법이다. 반복 행위를 어떻게 줄일 수 있을지 알아보자. </p>
<h2 id="💾--상수-데이터">💾  상수 데이터</h2>
<h3 id="상수-데이터란">상수 데이터란?</h3>
<p>상수 데이터는 변하지 않는 정적인 데이터를 말한다. 그렇기 때문에 백엔드와 API를 통해서 가져오지 않는 정적인 데이터들의 경우 상수데이터를 만들어서 효율적으로 구성할 수 있다. </p>
<h3 id="상수-데이터를-사용하는-이유">상수 데이터를 사용하는 이유?</h3>
<p>인스타그램 클론코딩을 하면서 불편했던 부분은 스토리 피드, 친구 추천, 피드 등을 만드는 과정에서 하드 코딩을 이용하다보니 엄청나게 긴 코드가 나오게 되는 점이었다. 이는 나중에 원하는 부분을 찾기도 어렵게 만들었을 뿐 아니라 전혀 개발자스럽지 못하다는 느낌이 들었다.(복사 &amp; 붙여넣기의 반복...) </p>
<p>이런 UI 부분을 상수 데이터와 <code>map()</code> 메소드를 이용하면 간단하게 표현할 수 있다. 수정이 필요한 경우에도 상수 데이터만 수정하면 반영이 되므로 유지보수에도 큰 장점이 있다. </p>
<p>상수 데이터를 사용하고 싶은 경우에는 <code>JS</code> 파일을 이용하여 작성하고 이를 <code>import</code> 해서 필요한 곳에서 사용하면 된다. 상수 데이터의 경우에는 구별을 위해서 대문자와 <code>_</code> 를 사용하는 것을 권장한다. </p>
<h2 id="📲--mock-데이터">📲  Mock 데이터</h2>
<h3 id="mock-데이터란">Mock 데이터란?</h3>
<p><code>Mock</code> 데이터는 가짜 데이터를 의미한다. 쉽게 말하면 백엔드 API로부터 데이터를 받아오기 전에 닮은 모습의 가짜 데이터를 말한다.</p>
<h3 id="mock-데이터를-사용하는-이유">Mock 데이터를 사용하는 이유</h3>
<p>개발을 진행하다 보면 많은 일이 발생하게 된다. 특히, 백엔드의 API가 아직 만들어지지 않은 상황임에도 불구하고 프론트엔드 개발도 계속 진행되어야 하는 경우도 있다. </p>
<p>이때, 데이터 값을 받아올 수 없기 때문에 하드코딩을 진행하여 화면을 구현하고 나중에 데이터를 이용하기 위해 바꿔야 한다면 정말... 답답한 일이 될 것이다. 
<img src="https://images.velog.io/images/jun_n3/post/9bb0f2bc-f0ab-4644-ba13-4f6a9b8a8781/giphy.gif" alt=""></p>
<p>데이터는 매일 먹는 아침 같은 것이 아니다. 프론트엔드 개발자는 API가 아직 없더라도 데이터를 화면에 예쁘게 보여주는 것이 중요하다. 그렇기 때문에 책임감을 가지고 <code>Mock</code> 데이터를 이용해보는 것이 좋다. </p>
<blockquote>
<p>💡 주의할 점!!
<code>Mock data</code> 혼자서 만들고 화면으로 구현한다고 모든 문제가 해결되지는 않는다. 만약 백엔드와 소통 없이 <code>Mock data</code> 를 만들고 결국 <code>key</code> 값들이 다르다면 무수한 에러들을 만나게 될 것이다. 꼭 사전에 소통하여 받을 데이터와 같은 <code>key</code> 들을 가질 수 있도록 하자.</p>
</blockquote>
<h2 id="🖨--mock-데이터-활용하기">🖨  Mock 데이터 활용하기</h2>
<h3 id="mock-데이터-예시">Mock 데이터 예시</h3>
<pre><code class="language-json">// json 파일
[
  {
    &quot;id&quot;: 1,
    &quot;profile&quot;: &quot;/images/cheoljinJu/food.jpeg&quot;,
    &quot;userName&quot;: &quot;CheoljinJu&quot;,
    &quot;url&quot;: &quot;/images/cheoljinJu/food.jpeg&quot;,
    &quot;comments&quot;: [
      {
        &quot;id&quot;: 1,
        &quot;userName&quot;: &quot;현재님&quot;,
        &quot;comment&quot;: &quot;안녕하세요!&quot;,
        &quot;isUser&quot;: false
      },
      {
        &quot;id&quot;: 2,
        &quot;userName&quot;: &quot;동희님&quot;,
        &quot;comment&quot;: &quot;오히려 좋아&quot;,
        &quot;isUser&quot;: false
      },
      {
        &quot;id&quot;: 3,
        &quot;userName&quot;: &quot;영현님&quot;,
        &quot;comment&quot;: &quot;이 노래 좋아합니다!&quot;,
        &quot;isUser&quot;: false
      },
      {
        &quot;id&quot;: 4,
        &quot;userName&quot;: &quot;무현님&quot;,
        &quot;comment&quot;: &quot;말만 하세요.&quot;,
        &quot;isUser&quot;: false
      }
    ]
  },
  {
    &quot;id&quot;: 2,
    &quot;profile&quot;: &quot;/images/cheoljinJu/cat.jpeg&quot;,
    &quot;userName&quot;: &quot;CatCat&quot;,
    &quot;url&quot;: &quot;/images/cheoljinJu/cat.jpeg&quot;,
    &quot;comments&quot;: [
      {
        &quot;id&quot;: 1,
        &quot;userName&quot;: &quot;승찬님&quot;,
        &quot;comment&quot;: &quot;반갑습니다!&quot;,
        &quot;isUser&quot;: false
      },
      {
        &quot;id&quot;: 2,
        &quot;userName&quot;: &quot;성환님&quot;,
        &quot;comment&quot;: &quot;감사합니다.&quot;,
        &quot;isUser&quot;: false
      },
      {
        &quot;id&quot;: 3,
        &quot;userName&quot;: &quot;세준님&quot;,
        &quot;comment&quot;: &quot;자전거 타고 싶습니다!&quot;,
        &quot;isUser&quot;: false
      },
      {
        &quot;id&quot;: 4,
        &quot;userName&quot;: &quot;연우님&quot;,
        &quot;comment&quot;: &quot;킥오프 기다리셨죠?&quot;,
        &quot;isUser&quot;: false
      }
    ]
  }
]</code></pre>
<p>위는 인스타그램 내에서 피드들을 가지고 있는 데이터를 간단하게 작성해본 예시이다. 데이터를 보면 2개의 피드가 있고 그 안에 <code>userName</code>, <code>profile</code>, <code>url</code> 등의 정보와 댓글들의 정보를 가지고 있는 <code>comments</code> 로 구성되어 있음을 알 수 있다. </p>
<h3 id="arraymap-이용하기">Array.map() 이용하기</h3>
<pre><code class="language-js"> &lt;section className=&quot;main__feeds&quot;&gt;
              {this.state.feeds.map(feed =&gt; (
                &lt;Feed key={feed.id} feed={feed} comments={feed.comments} /&gt;
              ))}
            &lt;/section&gt;</code></pre>
<p>우리는 여러 개의 <code>feed</code> 를 <code>map()</code> 을 이용해서 렌더링 할 예정이다. 그렇기 때문에 현재 <code>main</code> 페이지의 <code>state</code> 에서 <code>feeds</code> 를 가져와서 렌더링을 한다. 이때 고유한 <code>key</code> 값을 받을 수 있도록 <code>props</code> 로 전달했다. </p>
<pre><code class="language-js">componentDidMount = () =&gt; {
    fetch(&#39;http://localhost:3000/data/CommentDataCJ.json&#39;, {
      method: &#39;GET&#39;,
    })
      .then(result =&gt; result.json())
      .then(feeds =&gt; {
        this.setState({
          feeds,
        });
      });
  };</code></pre>
<p>위의 코드는 우리가 만든 <code>json</code> 데이터를 받아오는 <code>fetch</code> 함수이다. <code>componentDidMount()</code> 를 이용해서 컴포넌트가 화면에 렌더링 된 후에 데이터를 받고 이를 <code>setState()</code> 를 통해서 리렌더링이 일어나도록 했다. </p>
<p>이때, 우리는 데이터를 요청해서 받아만 올 예정이기 때문에 <code>method</code> 로 <code>GET</code> 를 사용했다. </p>
<h1 id="✨-마치며">✨ 마치며</h1>
<p>이렇게 우리는 <code>fetch</code> 를 이용해서 데이터를 받아오는 방법과 <code>componentDidMount</code> 가 언제 발생하게 되는지, 그리고 <code>Mock</code> 데이터와 <code>map()</code> 을 활용해서 어떻게 반복되는 작업을 줄이고 간결하게 만들 수 있는지 정리했다. </p>
<p>바닐라 <code>JavaScript</code>와 <code>HTML</code> 을 사용해서 인스타그램을 만드는 과정에서 정말 코드가 길어지고 가독성이 나빠졌던 경험을 했는데 이번 내용들을 통해서 이를 많이 개선할 수 있어서 좋은 경험이었다. </p>
<p><code>Mock 데이터</code> 와 <code>map()</code> 경우에는 정말 많은 활용성을 가지고 있기 때문에 꼭 기억하고 많은 활용을 해볼 예정이다. </p>
]]></description>
        </item>
    </channel>
</rss>