<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>코딩하는 고양이</title>
        <link>https://velog.io/</link>
        <description>개발괴발</description>
        <lastBuildDate>Mon, 14 Jul 2025 11:43:57 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>코딩하는 고양이</title>
            <url>https://velog.velcdn.com/images/developer-sora/profile/99d76023-400b-4316-becf-e19cc893de4a/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 코딩하는 고양이. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/developer-sora" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[TailwindCSS의 동적 스타일링을 위한 라이브러리 비교(clsx, cva, twMerge)]]></title>
            <link>https://velog.io/@developer-sora/TailwindCSS%EC%9D%98-%EB%8F%99%EC%A0%81-%EC%8A%A4%ED%83%80%EC%9D%BC%EB%A7%81%EC%9D%84-%EC%9C%84%ED%95%9C-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EB%B9%84%EA%B5%90clsx-cva-twMerge</link>
            <guid>https://velog.io/@developer-sora/TailwindCSS%EC%9D%98-%EB%8F%99%EC%A0%81-%EC%8A%A4%ED%83%80%EC%9D%BC%EB%A7%81%EC%9D%84-%EC%9C%84%ED%95%9C-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EB%B9%84%EA%B5%90clsx-cva-twMerge</guid>
            <pubDate>Mon, 14 Jul 2025 11:43:57 GMT</pubDate>
            <description><![CDATA[<h2 id="clsx">clsx</h2>
<p><code>clsx</code>는 조건부 스타일을 처리하는 아주 작은(239B) 유틸리티이다.
<code>classnames</code> 보다 작고 빠르다.
배열, 객체, 중첩 배열 전부 지원한다.</p>
<pre><code class="language-tsx">import clsx from &quot;clsx&quot;;

// 문자열
clsx(&#39;foo&#39;, true &amp;&amp; &#39;bar&#39;, &#39;baz&#39;);
//=&gt; &#39;foo bar baz&#39;

// 객체
clsx({ foo:true, bar:false, baz:isTrue() });
//=&gt; &#39;foo baz&#39;

// 배열
clsx([&#39;foo&#39;, 0, false, &#39;bar&#39;]);
//=&gt; &#39;foo bar&#39;</code></pre>
<p><code>clsx</code> 를 사용하지 않았을 때 조건부 처리를 할 경우</p>
<pre><code class="language-tsx">const className =
  &quot;base&quot; +
  (isActive ? &quot; bg-blue-500&quot; : &quot;&quot;) +
  (!isEnabled ? &quot; text-gray-400&quot; : &quot;&quot;);</code></pre>
<p>빈 문자열이나 false, undefined, null등이 생기는데, <code>clsx</code>는 이를 무시해준다.</p>
<h2 id="classnames">classnames</h2>
<p><code>classnames</code> 은 <code>clsx</code>와 비슷하지만 좀 더 오래되고 무거운 라이브러리이다.</p>
<p>문법상 거의 동일하니까 기존 코드에서 쓰고있는 경우를 제외하면 더 가볍고 빠른<code>clsx</code> 를 쓰자.</p>
<h2 id="cva">cva</h2>
<p><code>cva</code> 는 <code>class variance authority</code>의 약자로 안전한 UI 구성 요소를 구축하기 위한 라이브러리이다.</p>
<p><code>cva</code>는 작은 라이브러리지만 SSR/SSG 환경에서 사용하는게 좋다고 한다.</p>
<blockquote>
<p>Although cva is a tiny library, it&#39;s best to use in a SSR/SSG environment – your user probably doesn&#39;t need this JavaScript, especially for static components.</p>
</blockquote>
<p>CSR(클라이언트 사이드 렌더링)환경인 경우 <code>cva</code> 라이브러리 코드가 번들에 포함되고, 클래스 계산 로직이 브라우저에서 실행되어야 한다. 
즉, 사용자는 CSS 클래스를 위해 추가적으로 JavaScript를 다운로드해야 하는 것이다.</p>
<p>반면 SSR(서버사이드 렌더링)이나 SSG(정적 사이트 생성)환경의 경우 서버에서 미리 계산된 클래스가 HTML에 포함되어 전달된다. 
따라서 사용자는 별도의 JavaScript를 받을 필요가 없어 초기 로딩 성능이 향상되는 것이다.</p>
<p>그래서 정적 UI 중심이면 SSR/SSG에서 사용하는게 좋다!</p>
<h3 id="사용-예시">사용 예시</h3>
<pre><code class="language-tsx">import React from &quot;react&quot;;
import { cva, type VariantProps } from &quot;class-variance-authority&quot;;

const button = cva(&quot;button&quot;, {
  variants: {
    intent: {
      primary: [&quot;bg-blue-500&quot;, &quot;text-white&quot;, &quot;border-transparent&quot;],
      secondary: [&quot;bg-white&quot;, &quot;text-gray-800&quot;, &quot;border-gray-400&quot;],
    },
    size: {
      small: [&quot;text-sm&quot;, &quot;py-1&quot;, &quot;px-2&quot;],
      medium: [&quot;text-base&quot;, &quot;py-2&quot;, &quot;px-4&quot;],
    },
    disabled: {
      false: null,
      true: [&quot;opacity-50&quot;, &quot;cursor-not-allowed&quot;],
    },
  },
  // 특정 조합일 때만 적용할 클래스
  compoundVariants: [
    {
      intent: &quot;primary&quot;,
      disabled: false,
      class: &quot;hover:bg-blue-600&quot;,
    },
    {
      intent: &quot;secondary&quot;,
      disabled: false,
      class: &quot;hover:bg-gray-100&quot;,
    },
    { intent: &quot;primary&quot;, size: &quot;medium&quot;, class: &quot;uppercase&quot; },
  ],
  // 기본값
  defaultVariants: {
    disabled: false,
    intent: &quot;primary&quot;,
    size: &quot;medium&quot;,
  },
});

export interface ButtonProps
  extends Omit&lt;React.ButtonHTMLAttributes&lt;HTMLButtonElement&gt;, &quot;disabled&quot;&gt;,
    VariantProps&lt;typeof button&gt; {}

export const Button: React.FC&lt;ButtonProps&gt; = ({
  className,
  intent,
  size,
  disabled,
  ...props
}) =&gt; (
  &lt;button
    className={button({ intent, size, disabled, className })}
    disabled={disabled || undefined}
    {...props}
  /&gt;
);</code></pre>
<h2 id="twmerge">twMerge</h2>
<p>tailwind의 클래스 이름이 겹칠때 중복을 제거해서 가장 마지막 클래스만 남겨주는 라이브러리이다.</p>
<pre><code class="language-tsx">import { twMerge } from &quot;tailwind-merge&quot;;

twMerge(&quot;bg-red-500 bg-blue-500&quot;); 
// =&gt; &quot;bg-blue-500&quot;</code></pre>
<p>보통 twMerge와 clsx를 같이 사용한 유틸 함수를 만들어서 사용한다.</p>
<pre><code class="language-tsx">export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(...inputs));
}</code></pre>
<p>이렇게 하면 조건부 로직을 안전하게 처리할 수 있다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Query 에러 처리 개선기]]></title>
            <link>https://velog.io/@developer-sora/React-Query-%EC%97%90%EB%9F%AC-%EC%B2%98%EB%A6%AC-%EA%B0%9C%EC%84%A0%EA%B8%B0</link>
            <guid>https://velog.io/@developer-sora/React-Query-%EC%97%90%EB%9F%AC-%EC%B2%98%EB%A6%AC-%EA%B0%9C%EC%84%A0%EA%B8%B0</guid>
            <pubDate>Thu, 23 Jan 2025 07:07:43 GMT</pubDate>
            <description><![CDATA[<h1 id="react-query-에러-처리-개선기">React Query 에러 처리 개선기</h1>
<h2 id="배경">배경</h2>
<p>이번에 어드민 페이지의 결제 파트를 맡게 되면서, 결제, 환불, 환불 접수, 환불 접수 취소 등 사용자와의 상호작용이 많은 기능을 개발하게 되었다. 초기에는 기획대로 단순히 <strong>&quot;XX가 실패했습니다&quot;</strong>라는 에러 메시지만 보여주었으나, QA 과정에서 문제가 발견되었다.</p>
<p>예를 들어 특정 계정의 센터 ID와 환불 대상 센터 ID가 다를 때 <strong>&quot;환불이 실패했습니다&quot;</strong>라는 모달만 표시되어 원인을 알기 어려웠다. 네트워크 탭을 확인해보니 센터 ID 불일치로 인해 권한이 없어 실패한 경우였고, 같은 센터 ID라면 정상적으로 환불이 이루어지고 있었다. 즉, 환불 프로세스 자체의 문제가 아니라 권한 문제였던 것이다.</p>
<p>이러한 경우 정확한 실패 사유를 사용자에게 전달하는 것이 사용자 경험에 더 좋을 것이라고 판단했고, 기획자와 논의한 끝에 적절한 오류 메시지를 표시하도록 결정하였다.</p>
<p>그렇게 <code>error.message</code>를 표시하면 되는 줄 알았으나..!! 문제가 있었다.</p>
<h3 id="1-에러-응답-포맷-변경">1. 에러 응답 포맷 변경</h3>
<p>기존의 에러 응답 타입은 아래와 같았다.</p>
<pre><code class="language-typescript">type ApiErrorType = {
  detail: {
    error_code: string;
    message: string;
    status_code: number;
  };
};</code></pre>
<p>하지만 이번에 결제 개발에 들어가면서 백엔드에서 글로벌 익셉션 핸들러를 도입해 에러 응답 타입이 아래와 같이 바뀐 것이다.</p>
<pre><code class="language-typescript">type NewErrorType = {
  STATUS_CODE: number;
  ERROR_CODE: string;
  MESSAGE: string;
  ERROR_CLASS: string;
};</code></pre>
<p><code>detail</code>이 빠지고 필드명이 대문자로 변경된 것이다!
앞으로 모든 api에 저 포맷이 적용될 예정이라 에러 응답 타입을 받는 <code>apiFetch</code>(커스텀 fetch 함수)에서 이를 변환해주는 작업이 필요했다.</p>
<p>그래서 <code>apiFetch</code> 함수를 수정하기 위해 자세히 살펴본 결과...</p>
<h3 id="2-에러-처리-흐름-파편화-문제">2. 에러 처리 흐름 파편화 문제</h3>
<h4 id="fetch-함수">fetch 함수</h4>
<pre><code class="language-typescript">const apiFetch = () =&gt; {
  // fetch 로직 ...
  if (!response.ok) {
    if (statusCode &gt;= 500) errorHandler(&quot;서버 오류가 발생했습니다.&quot;);
    switch (errorCode) {
      case &quot;토큰 에러&quot;: {
        errorHandler(errorMessage, true);
        break;
      }
      // 다른 에러들..
      default: {
        errorHandler(errorMessage);
      }
    }
    return Promise.reject(error);
  }
};

const errorHandler = async (message: string, isLogout: boolean) =&gt; {
  const { setCommonError } = useCommonStore.getState();
  setCommonError({
    title: message,
    isLogout,
  });
};</code></pre>
<p>api 요청이 실패했을 경우 전역 상태에 에러 정보를 저장하고, 이를 팝업 컨텍스트 <code>PopupContext</code>에서 감지해 팝업을 띄우는 방식이었다.</p>
<h4 id="전역-팝업-컨텍스트">전역 팝업 컨텍스트</h4>
<pre><code class="language-javascript">const PopupContextProvider = ({ children }: PopupPropsType) =&gt; {
  const { commonError, setCommonError } = useCommonStore.getState();

  const handleErrorPopup = () =&gt; {
    if (!commonError) return;
    openPopup(commonError);
    if (commonError.isLogout) {
      navigate(&quot;/login&quot;);
      setCommonError(null);
    }
  };

  useEffect(() =&gt; {
    handleErrorPopup();
  }, [commonError]);

  return (
    &lt;PopupContext.Provider value={contextValues}&gt;
      &lt;Popup /&gt;
    &lt;/PopupContext.Provider&gt;
  );
};</code></pre>
<p>아마 함수 안에서 모달을 띄우는 커스텀훅인 <code>usePopup</code>을 쓰지 못해서 전역으로 에러 상태를 전달해준 뒤에 팝업 컨텍스트에서 <code>useEffect</code>로 해당 상태를 감지해서 띄워준 것 같았다.</p>
<p>하지만.. 이게 최선인가? 싶었다.</p>
<p>이렇게 에러 처리의 흐름이 분산되어있으면 흐름을 파악하기 어렵고, 새로운 타입의 에러 처리가 필요할 때 어디에 구현해야 할 지 고민이 필요했다.</p>
<p>또한 <code>apiFetch</code> 함수는 api를 호출하는 역할에 집중하고, <code>PopupContext</code>는 팝업을 렌더링하는데만 집중하고 싶었다.</p>
<p>그래서 글로벌 에러 핸들러를 도입하면 에러의 단일 진입점을 확보해 흐름을 파악하는데 더 용이하지 않을까? 하고 생각했다.</p>
<h2 id="적용-과정">적용 과정</h2>
<h3 id="에러-타입-포맷팅">에러 타입 포맷팅</h3>
<p>기존 에러 타입과 새로운 에러 응답 타입을 통합하기 위해 변환 함수를 만들었다.</p>
<pre><code class="language-typescript">const DEFAULT_MESSAGE = &quot;잘못된 접근입니다.&quot;;

export const formatError = (
  errorData: ApiErrorType | NewErrorType
): ErrorResponseType =&gt; {
  const errorInfo =
    &quot;detail&quot; in errorData
      ? {
          detail: {
            status_code: errorData.detail?.status_code,
            error_code: errorData.detail?.error_code,
            message: errorData.detail?.message || DEFAULT_MESSAGE,
          },
        }
      : {
          detail: {
            status_code: errorData.STATUS_CODE,
            error_code: errorData.ERROR_CODE,
            message: errorData.MESSAGE || DEFAULT_MESSAGE,
          },
        };
  return errorInfo;
};</code></pre>
<p>현재 아주 많은 곳에서 예전 에러 타입을 쓰고 있었기 때문에.. 반환하는 에러 타입을 기존 에러 타입을 기준으로 확장시키기로 했다.</p>
<p><code>detail</code>이 불필요하다고 생각해서 없앴더니 기존에 detail로 접근하는 코드가 있어서 에러 메시지가 undefined로 나온 것이였다.</p>
<p>마이그레이션을 할 때 생각보다 많은 것을 고려해야하고 테스트도 많이 해봐야하는구나를 느꼈다..</p>
<h3 id="global-callbacks">Global callbacks</h3>
<p>새로운 <code>QueryClient</code>를 생성할 때 <code>mutationCache</code>나 <code>queryCache</code> 속성으로 <code>onError</code>를 정의해주면 중앙에서 에러 처리를 관리할 수 있다.</p>
<pre><code class="language-typescript">const queryClient = new QueryClient({
  queryCache: new QueryCache({
    onError: (error) =&gt; openPopup(error.message),
  }),
  mutationCache: new MutationCache({
    onError: (error) =&gt; openPopup(error.message),
  }),
});</code></pre>
<h3 id="custom-hook과-함께-사용하기">Custom hook과 함께 사용하기</h3>
<p>우리의 어드민 페이지는 모든 에러 처리를 하나의 공통된 팝업 창으로 해결하도록 기획되어있다. 해당 팝업은 <code>usePopup</code> hook으로 호출할 수 있는데, 훅은 컴포넌트 외부에서 호출할 수 없어서 <code>QueryClientProvider</code>를 감싸는 함수 컴포넌트 <code>CustomQueryClientProvider</code>를 만들어주었다.</p>
<pre><code class="language-typescript">const CustomQueryClientProvider = ({ children }: { children: ReactNode }) =&gt; {
  const { openPopup } = usePopup();

  // 최초 렌더링 시에만 초기화하기 위해 useState 사용
  const [queryClient] = useState(() =&gt; {
    return new QueryClient({
      queryCache: new QueryCache({
        onError: (error) =&gt;
          openPopup({
            title: error.message,
          }),
      }),
    });
  });

  return (
    &lt;QueryClientProvider client={queryClient}&gt;{children}&lt;/QueryClientProvider&gt;
  );
};

export default CustomQueryClientProvider;</code></pre>
<p>위의 컴포넌트를 기존의 <code>QueryClientProvider</code>와 교체하면 된다.</p>
<pre><code class="language-typescript">  &lt;PopupContextProvider&gt;
    &lt;CustomQueryClientProvider&gt;
      &lt;App /&gt;
    &lt;/CustomQueryClientProvider&gt;
  &lt;/PopupContextProvider&gt;</code></pre>
<p>처음에는 QueryClient를 생성하는 곳에서 바로 에러 처리 로직을 작성하려고 했는데, 에러 종류에 따라 처리 방식이 조금씩 달라서 <code>useErrorHandlers</code>라는 커스텀 훅을 분리해서 여기서만 에러 처리 로직을 작성하기로 했다.</p>
<pre><code class="language-typescript">export const useErrorHandlers = () =&gt; {
  const { open: openPopup } = usePopup();

  return (error: ErrorResponseType | Error) =&gt; {
    // 서버 에러
    if (error.status_code &gt;= 500) {
      return openPopup({
        title: &quot;서버와 통신 중 에러가 발생했습니다.&quot;,
      });
    }
    // 기본 에러 메시지
    openPopup({
      title: error.message,
    });
    // 그 외 다양한 처리 로직...
  };
};</code></pre>
<p>그리고 <code>useErrorHandlers</code>을 <code>new QueryClient</code>생성 시 활용한다.</p>
<pre><code class="language-typescript">const queryClient = useMemo(
  () =&gt;
    new QueryClient({
      mutationCache: new MutationCache({
        onError: handleError,
      }),
      queryCache: new QueryCache({
        onError: handleError,
      }),
    }),
  [handleError]
);</code></pre>
<h3 id="에러-메시지-커스터마이징-하기">에러 메시지 커스터마이징 하기</h3>
<p><code>meta</code> 필드를 사용하면 쿼리 별로 원하는 에러 메시지를 보여줄 수 있다.</p>
<pre><code class="language-typescript">useMutation({
  mutationFn: payment,
  meta: {
    errorMessage: &quot;결제에 실패했어요.&quot;,
  },
});</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Jest에 대하여(함수, matcher, 테스트 커버리지)]]></title>
            <link>https://velog.io/@developer-sora/Jest%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC%ED%95%A8%EC%88%98-matcher-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80</link>
            <guid>https://velog.io/@developer-sora/Jest%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC%ED%95%A8%EC%88%98-matcher-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80</guid>
            <pubDate>Sun, 15 Sep 2024 11:36:01 GMT</pubDate>
            <description><![CDATA[<h2 id="테스트-코드를-작성하는-이유">테스트 코드를 작성하는 이유</h2>
<p>테스트 코드는 <strong>버그를 사전에 잡아줄 수 있고</strong>, <strong>코드 품질을 유지</strong>하며, 리팩토링시 <strong>안정성을 확보</strong>하는 데 중요한 역할을 한다.</p>
<p>또한 잘 작성된 테스트 코드는 그 자체로 명세가 되기도 한다. 다른 개발자들이 테스트 코드를 보고 코드의 동작을 쉽게 이해할 수 있기 때문이다.</p>
<p><del>그리고 일일히 손으로 직접 테스트 하는 것이 끔찍하기 때문에 테스트 코드를 작성하는 것도 있다.</del></p>
<p>따라서 테스트 코드를 잘 작성하는 것은 중요하다는 것을 알 수 있다!</p>
<h2 id="jest란">Jest란?</h2>
<p>페이스북에서 만든 Javascript 테스팅 프레임워크이다. </p>
<p>jest 라이브러리 하나만 설치하면 <strong>Test Runner</strong>와 <strong>Test Matcher</strong>, <strong>Test mock</strong>까지 제공해주기 때문에 테스트 환경을 쉽게 구축할 수 있다.</p>
<blockquote>
<p><strong>Test Runner</strong>: 테스트 파일을 찾아서 실행하고 성공 또는 실패 여부를 알려주는 도구를 말한다.
<strong>Test Matcher</strong>: 테스트의 기대 결과를 표현하는 구문을 의미한다. 보통 특정 조건이 맞는지 확인하는 역할을 한다.
<strong>Test mock</strong>: 실제 객체나 함수 대신 mock 객체나 함수를 사용하여 테스트하는 기법을 말한다.</p>
</blockquote>
<p>또한 jest는 테스트 코드를 병렬로 실행하여 테스트 시간을 단축시킨다.</p>
<p>따라서 간편하고 빠른 jest를 프로젝트에 적용하기로 하였고, 제대로 사용하기 위해 jest에 대해 학습하기로 하였다.</p>
<h2 id="jest-설치-및-적용">Jest 설치 및 적용</h2>
<pre><code class="language-shell">npm i -D jest</code></pre>
<p>Jest를 개발 의존성으로 설치한다.</p>
<pre><code class="language-javascript">  &quot;scripts&quot;: {
      &quot;test&quot;: &quot;jest --verbose&quot; // 테스트 결과를 자세히 출력하는 옵션
  },</code></pre>
<p><code>package.json</code>파일에 <code>test</code> 스크립트를 <code>jest</code>로 설정하면 된다.</p>
<p>이후 테스트를 실행하고 싶으면 터미널에 <code>npm test</code>를 입력해서 <code>jest</code>를 실행하면 된다.</p>
<h2 id="jest-함수">Jest 함수</h2>
<p>지금까지 describe와 test만 사용했었는데, 혹시 다른 함수가 있는지 찾아보았다.</p>
<h3 id="beforeeach-aftereach">beforeEach(), afterEach()</h3>
<p><code>beforeEach()</code>는 <strong>각 테스트 실행 전</strong>에 공통 작업을 수행한다. 
이는 테스트 환경을 항상 일정하게 유지할 수 있게 해준다.</p>
<pre><code class="language-javascript">beforeEach(() =&gt; {
  // 각 테스트 전에 실행되는 코드
  initializeSomething();
});

test(&#39;첫 번째 테스트&#39;, () =&gt; {
  // beforeEach 실행 후에 실행됨
  expect(something).toBe(true);
});

test(&#39;두 번째 테스트&#39;, () =&gt; {
  // beforeEach 실행 후에 실행됨
  expect(something).toBe(false);
});</code></pre>
<p><code>afterEach()</code>는 <strong>각 테스트 실행 후</strong>에 작업을 수행한다.
메모리 정리나 테스트 상태 초기화 등에 사용된다.</p>
<pre><code class="language-javascript">afterEach(() =&gt; {
  // 각 테스트 후에 실행되는 코드
  cleanup();
});

test(&#39;첫 번째 테스트&#39;, () =&gt; {
  expect(something).toBe(true);
  // afterEach 실행
});

test(&#39;두 번째 테스트&#39;, () =&gt; {
  expect(something).toBe(false);
  // afterEach 실행
});</code></pre>
<h3 id="beforeall-afterall">beforeAll(), afterAll()</h3>
<p><code>beforeAll()</code>는 <strong>모든 테스트가 실행되기 전에 딱 한번</strong> 실행된다.
테스트 전반에 필요한 설정 작업을 할 때 사용된다.</p>
<pre><code class="language-javascript">beforeAll(() =&gt; {
  // 모든 테스트 전에 딱 한 번 실행되는 코드
  setupGlobalResources();
});</code></pre>
<p><code>afterAll()</code>는 *<em>모든 테스트가 끝난 후에 딱 한번 *</em> 실행된다. 
테스트 완류 후에 필요한 정리 작업을 수행하는데 사용된다.</p>
<pre><code class="language-javascript">afterAll(() =&gt; {
  // 모든 테스트 후에 딱 한 번 실행되는 코드
  teardownGlobalResources();
});</code></pre>
<h3 id="디버깅-용-only-skip">디버깅 용 only(), skip()</h3>
<p>테스트 코드를 디버깅할 때 사용하는 함수이다.</p>
<p>테스트 함수 중 하나만 실행하고 싶은 경우 <code>only()</code>를 사용하면 <code>only()</code>가 붙은 함수만 실행된다.</p>
<pre><code class="language-javascript">test.only(&quot;run only&quot;, () =&gt; {
  // 이 테스트 함수만 실행됨
});

test(&quot;not run&quot;, () =&gt; {
  // 실행 안됨
});</code></pre>
<p>반대로 한 함수를 제외하고 실행하고 싶은 경우 <code>skip()</code>을 사용하면 <code>skip()</code>이 붙은 함수를 제외하고 다른 테스트 함수들이 실행된다.</p>
<pre><code class="language-javascript">test.skip(&quot;skip&quot;, () =&gt; {
  // 이 테스트 함수는 제외됨
});

test(&quot;run&quot;, () =&gt; {
  // 실행됨
});</code></pre>
<h3 id="describe-it-test">describe(), it(), test()</h3>
<p>테스트 함수가 여러개 작성되어 있는 경우, <code>describe()</code> 함수로 묶어서 실행할 수 있다.</p>
<pre><code class="language-javascript">describe(&quot;group 1&quot;, () =&gt; {
    test(&quot;test1-1&quot;, () =&gt; {
        // ...
    });

    test(&quot;test1-1&quot;, () =&gt; {
        // ...
    });
})</code></pre>
<p><code>test()</code>와 <code>it()</code>은 테스트 함수를 실행하는 역할을 한다. <code>test()</code>와 <code>it()</code>이 다른 줄 알았는데 완전 <strong>동일</strong>하다고 한다!</p>
<h3 id="함수-모킹-fn-spyon">함수 모킹 fn(), spyOn()</h3>
<p>jest에서 가짜 함수(mock function)를 생성하거나 기존 함수의 동작을 감시할 때 사용한다.</p>
<ol>
<li><code>jest.fn()</code></li>
</ol>
<p>임의의 <strong>가짜 함수를 생성</strong>할 때 사용한다. 
호출 여부, 호출 횟수, 전달된 인자 등을 추적할 수 있으며, 원하는 동작을 추가하거나 반환 값을 설정할 수 있다.</p>
<pre><code class="language-javascript">const mockFunction = jest.fn();

mockFunction(&#39;arg1&#39;, &#39;arg2&#39;);
// 호출 여부 추적
expect(mockFunction).toHaveBeenCalled(); // ✔️ 
// 전달된 인자 추적
expect(mockFunction).toHaveBeenCalledWith(&#39;arg1&#39;, &#39;arg2&#39;); // ✔️
// 호출 횟수 추적
expect(mockFunction).toHaveBeenCalledTimes(1); // ✔️

// 반환 값 설정
const mockFunction2 = jest.fn().mockReturnValue(42);
expect(mockFunction2()).toBe(42); // ✔️

//함수 구현 설정
const mockFunction3 = jest.fn((x) =&gt; x * 2);
expect(mockFunction3(2)).toBe(4); // ✔️</code></pre>
<p><code>jest.fn()</code>함수는 주로 콜백 함수나 직접 구현한 함수를 테스트 할 때 사용된다.</p>
<ol start="2">
<li><code>jest.spyOn()</code>
객체의 특정 메서드를 <strong>감시(spy)하거나 모킹</strong> 할 때 사용한다.
해당 메서드가 어떻게 호출되는지 추적할 수 있고, 필요에 따라 메서드의 동작을 대체할 수도 있다.</li>
</ol>
<pre><code class="language-javascript">const myObject = {
  method: () =&gt; &#39;original value&#39;,
};

// myObject.method를 감시하는 spy 생성
const spy = jest.spyOn(myObject, &#39;method&#39;);

// 메서드 호출
myObject.method();

// 메서드가 호출되었는지 확인
expect(spy).toHaveBeenCalled(); // ✔️</code></pre>
<h4 id="실제-사용-예시">실제 사용 예시</h4>
<p>model 클래스의 <code>create</code> 메서드의 id가 현재 시간을 기준으로 생성되는데, 테스트를 하는 경우 <code>create</code> 메서드가 실행된 시간과 테스트가 실행된 시간이 맞지 않아 <code>toEqual()</code> 비교에 실패하는 경우가 발생하였다.</p>
<p><strong>모델 클래스</strong></p>
<pre><code class="language-javascript">export default class Model {
  ...
  create(title) {
    const newTodos = {
      title,
      completed: false,
      id: Date.now(),
    };
    this.storage.add(newTodos);
  }
}</code></pre>
<p><strong>실패한 테스트 코드</strong></p>
<pre><code class="language-javascript">  test(&#39;create 메서드가 호출되면 store.add가 새로운 항목을 추가한다&#39;, () =&gt; {
    const title = &#39;test1&#39;;
    model.create(title);
    expect(store.add).toHaveBeenCalledWith({
      title,
      completed: false,
      id: Date.now(),
      // 테스트 실패
      //  -   &quot;id&quot;: 1726398292209,
      //  +   &quot;id&quot;: 1726398292208,
    });
  });</code></pre>
<p>이를 해결하기 위해 <code>Date.now()</code> 메서드를 모킹하여서 <code>Date.now()</code>가 호출될 때마다 실제 현재 시간을 반환하는 대신 지정된 값을 반환하도록 하였다.</p>
<pre><code class="language-javascript">jest.spyOn(Date, &#39;now&#39;).mockReturnValue(1)</code></pre>
<p>이렇게 하면 <code>Date.now()</code>가 호출되면 1이 반환된다.</p>
<p><strong>성공한 테스트 코드</strong></p>
<pre><code class="language-javascript">  test(&#39;create 메서드가 호출되면 store.add가 새로운 항목을 추가한다&#39;, () =&gt; {
    const title = &#39;test1&#39;;
    jest.spyOn(Date, &#39;now&#39;).mockReturnValue(1);
    model.create(title);
    expect(store.add).toHaveBeenCalledWith({
      title,
      completed: false,
      id: 1,
    });
    // 모킹된 Date.now()를 원래 상태로 복원
    jest.restoreAllMocks();
  });</code></pre>
<h3 id="모듈-모킹-mock">모듈 모킹 mock()</h3>
<p>함수 단위가 아닌 모듈 단위로 모킹을 하고 싶을 경우에 사용한다.</p>
<pre><code class="language-javascript">import { fetchData } from &#39;./api&#39;;
jest.mock(&#39;./api&#39;); // &#39;./api&#39; 모듈의 모든 함수를 모킹</code></pre>
<h2 id="matcher">Matcher</h2>
<h3 id="tobe">toBe()</h3>
<p>동등 비교를 할 때 사용한다. Javascript의 <code>===</code> 연산자와 동일하게 동작한다.</p>
<pre><code class="language-javascript">expect(1 + 1).toBe(2); // ✔️</code></pre>
<h3 id="toequal">toEqual</h3>
<p>객체나 배열 같은 참조형 데이터 구조를 비교할 때 사용한다.</p>
<pre><code class="language-javascript">expect({ a: 1 }).toEqual({ a: 1 });  // ✔️
expect([1, 2]).toEqual([1, 2]);  // ✔️</code></pre>
<h3 id="tobetruthy-tobefalsy">toBeTruthy(), toBeFalsy()</h3>
<p><code>truthy</code>하거나 <code>falsy</code>한 값을 확인하고 싶을 때 사용한다.</p>
<pre><code class="language-javascript">expect(&#39;hello&#39;).toBeTruthy();  // ✔️
expect(null).toBeFalsy();  // ✔️</code></pre>
<h3 id="tohavelength-tocontain">toHaveLength(), toContain()</h3>
<p><code>toHaveLength</code>는 배열이나 문자열의 길이를 검사하고, <code>toContain</code>은 배열이나 문자열이 특정 요소를 포함하는지 확인할 때 사용한다.</p>
<pre><code class="language-javascript">expect([1, 2, 3]).toHaveLength(3);  // ✔️
expect([1, 2, 3]).toContain(2);  // ✔️
expect(&#39;hello world&#39;).toContain(&#39;hello&#39;);  // ✔️</code></pre>
<h3 id="tothrow">toThrow()</h3>
<p>특정 함수가 에러를 던지는지 확인할 때 사용한다. 인자로 문자열을 넘기면 예외 메시지를 비교한다.</p>
<pre><code class="language-javascript">function throwError() {
  throw new Error(&#39;Something went wrong&#39;);
}

expect(throwError).toThrow();  // ✔️
expect(throwError).toThrow(&#39;Something went wrong&#39;);  // ✔️
expect(throwError).toThrow(&#39;Success!&#39;); // ❌</code></pre>
<h3 id="비동기-코드-테스트-resolves-rejects">비동기 코드 테스트 resolves(), rejects()</h3>
<p>비동기 Promise가 성공했는지, 실패했는지 확인할 때 사용한다.</p>
<pre><code class="language-javascript">
// 성공적인 Promise 처리
const fetchData = () =&gt; {
  return Promise.resolve(&#39;data received&#39;);
};

test(&#39;fetches the correct data with resolves&#39;, () =&gt; {
  return expect(fetchData()).resolves.toBe(&#39;data received&#39;);
}); // ✔️

// 실패하는 Promise 처리
const fetchError = () =&gt; {
  return Promise.reject(&#39;fetch failed&#39;);
};

test(&#39;handles fetch error with rejects&#39;, () =&gt; {
  return expect(fetchError()).rejects.toBe(&#39;fetch failed&#39;);
}); // ✔️
</code></pre>
<h2 id="테스트-커버리지">테스트 커버리지</h2>
<h3 id="테스트-커버리지란">테스트 커버리지란?</h3>
<p>소프트웨어 테스트에서 코드가 얼만큼 테스트되고 있는지를 나타내는 지표이다.
테스트 커버리지가 높은 소프트웨어는 버그가 발생할 확률이 적어 신뢰도 높은 소프트웨어라고 할 수 있다.</p>
<h3 id="jest-테스트-커버리지-측정하기">Jest 테스트 커버리지 측정하기</h3>
<pre><code class="language-shell">jest --coverage</code></pre>
<h3 id="jest의-테스트-커버리지-유형">Jest의 테스트 커버리지 유형</h3>
<ul>
<li><p>문장(Statement) 커버리지: 코드에서 실행된 모든 <strong>개별 문장</strong>의 비율을 측정한다.</p>
</li>
<li><p>분기(Branch) 커버리지: 조건문(<code>if</code>, <code>else</code>, <code>switch</code>)의 모든 <strong>분기</strong>가 테스트 되었는지 확인한다.</p>
</li>
<li><p>함수(Function) 커버리지: 코드에서 정의된 <strong>함수나 메서드</strong>가 호출된 비율을 나타낸다.</p>
</li>
<li><p>라인(Line) 커버리지: 코드에서 실행된 <strong>라인</strong>의 비율을 나타낸다.</p>
</li>
</ul>
<blockquote>
<p>참조
<a href="https://inpa.tistory.com/entry/JEST-%F0%9F%93%9A-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%97%90-%EC%9C%A0%EC%9A%A9%ED%95%9C-%ED%95%A8%EC%88%98-only-skip-describe-it">https://inpa.tistory.com/entry/JEST-%F0%9F%93%9A-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%97%90-%EC%9C%A0%EC%9A%A9%ED%95%9C-%ED%95%A8%EC%88%98-only-skip-describe-it</a>
<a href="https://www.daleseo.com/jest-basic/">https://www.daleseo.com/jest-basic/</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[테오의 스프린트 16기 회고(4cus)]]></title>
            <link>https://velog.io/@developer-sora/%ED%85%8C%EC%98%A4%EC%9D%98-%EC%8A%A4%ED%94%84%EB%A6%B0%ED%8A%B8-16%EA%B8%B0-%ED%9B%84%EA%B8%B04cus</link>
            <guid>https://velog.io/@developer-sora/%ED%85%8C%EC%98%A4%EC%9D%98-%EC%8A%A4%ED%94%84%EB%A6%B0%ED%8A%B8-16%EA%B8%B0-%ED%9B%84%EA%B8%B04cus</guid>
            <pubDate>Sat, 24 Aug 2024 15:07:38 GMT</pubDate>
            <description><![CDATA[<h1 id="이제야-올리는-이유">이제야 올리는 이유</h1>
<p>예전에 쓰다가 임시저장 해놓고 까먹고있었다.(...) 
쓰던게 아까워서 마저 써서 올리기로 함!</p>
<h1 id="동기">동기</h1>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/6ffcbe95-3028-4353-8287-706ab0db8f16/image.png" alt=""></p>
<p>테오의 프론트엔드 오픈채팅 방에서 스프린트 모집 글을 보았다. 원래 스터디나 프로젝트를 좋아하기도 하고 날짜와 시간 모두 가능해서(<del>백수</del>) 신청하였다. </p>
<p>프로젝트를 예전부터 하고싶었는데 팀원 모집하기도 어렵고 지속하기도 힘들어서 미루고 있었는데 좋은 기회라고 생각했다. 그리고 단 5일이라서 프로젝트를 하다가 <del>중간에 취업하면 어쩌지?</del> 라는 부담이 없었다.</p>
<h1 id="주제">주제</h1>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/37d29dc0-9749-4bb2-9567-056499efd46b/image.png" alt=""></p>
<p>첫째 날에 각자 준비해온 아이디어를 소개하고 팀빌딩을 하였다.
네컷앨범과 세어봤(다녀온 해외 여행 기록), 막차타(막차 시간 알림 서비스)중에 고민을 했는데 가장 먼저 느낌이 왔던 <strong>네컷앨범</strong>으로 정했다.</p>
<p>친구들이랑 만나면 거의 반강제로 인생네컷을 찍을 정도로 인생네컷을 좋아해서 어머 이건 해야해! 하고 생각했다.</p>
<p>그리고 y2k랑 키치한거 좋아해서 그런 감성으로 개발하면 너무 재밌을 것 같았다.
<img src="https://velog.velcdn.com/images/developer-sora/post/16c526b8-afe5-4e61-ae47-495ee6858e2e/image.png" alt=""></p>
<p>(대충 이런 느낌을 사랑함)</p>
<p>우리팀은 디자이너 1명, 백엔드 1명, 프론트엔드 5명으로 필요한 인원은 다 있었다. </p>
<p>황금밸런스!!
<img src="https://velog.velcdn.com/images/developer-sora/post/8bf950c2-3d48-404c-802e-35bc82ad5582/image.png" alt=""></p>
<p>(프론트엔드에게 소중한 백엔드와 디자이너)</p>
<h1 id="1일-차">1일 차</h1>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/89d3ba51-66e8-4883-996d-31b7425ae933/image.png" alt=""></p>
<p>팀원 소개와 강점과 약점, 규칙과 목표 등을 정했다.</p>
<p>한 칸을 쓰는데 거의 2분 정도밖에 안 줘서 크게 고민을 안하고 생각나는 대로 바로 적었다. 
근데 시간을 많이 줬어도 결과는 비슷했을 것 같다.</p>
<p>마침 다들 취업&amp;이직 준비 중이라 내적친밀감도 생기고 스케쥴에 대한 부담도 없었다.</p>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/3e9e7be0-4b56-4433-9877-151187990f1f/image.png" alt=""></p>
<p>우리 팀의 궁극적 목표는 &#39;<strong>사진을 꾸미고 저장하고 공유하기</strong>&#39;로 좁혀졌다. </p>
<h1 id="2일-차">2일 차</h1>
<p>1일 차 때 두루뭉술하게 목표를 잡았다면 2일 차 때는 그것을 구체화하고 스토리화 하는 작업을 하였다.</p>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/540a1daf-f6cf-418c-a8a8-2d5914534be1/image.png" alt=""></p>
<ol>
<li><p>내가 생각하는 우리 서비스의 궁극적인 목적은 무엇일까요?</p>
</li>
<li><p>내가 생각하는 우리가 만들 서비스의 주요대상은 누구일까요?</p>
</li>
<li><p>고객 여정 맵 (기존에 우리들의 대상은 목적을 이루기 위해서 어떻게 했나요? 
어떤 생각으로 시작해 어떤 단계를 밟고 어떤 부분에 만족하고 어떤 부분에서 힘들어하며 우리는 어떻게 도와줄수 있을까요? (단계, 행동, 생각, 행복한 순간, 고통의 순간, 기회)</p>
</li>
<li><p>내가 생각하는 우리가 만들 서비스가 추구해야 할 핵심가치는?</p>
</li>
</ol>
<p>이런 질문들에 답을 하면서 점차 서비스가 구체화되는 것을 느꼈다.</p>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/36ad02f0-f683-40de-aca3-4cdfcfe63a99/image.png" alt=""></p>
<p>처음에는 결정을 하지 않고 모두의 생각을 적는 것이 비효율적이라고 생각했는데, 이 과정이 있어서 자신의 의견이 타인의 의견에 동요되지 않고(하나의 정답으로 향하지 않고) 자신만의 의견을 낼 수 있게 한 것 같다.</p>
<p>덕분에 이런 것도 말해도 될까?싶은 것들도 가감없이 말할 수 있었다.</p>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/a2bfcc7e-444c-4293-aa03-2dc613865c47/image.png" alt=""></p>
<p>그리고 스토리보드를 작성했는데 사용자 시나리오가 보이니까 대충 우리가 무엇을 개발해야 할 지 보이기 시작했다.</p>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/2e114310-476f-4140-a8ce-237a5d7fd07e/image.png" alt=""></p>
<h1 id="3일-차">3일 차</h1>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/c738b473-104d-4973-9b7a-4a39bc2257e5/image.png" alt=""></p>
<p>준비해온 스케치를 발표하는데 다들 신박한 아이디어를 준비해오셨다.
근데 역시 디자이너라서 그런지 디자이너 분(왕굴👍)의 스케치가 가장 마음에 들었다.</p>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/dfcc6e45-314e-45b5-9137-9384209601ce/image.png" alt=""></p>
<p>왕굴의 스케치를 토대로 <strong>BDD</strong>(Behavior Driven Development, 행위 주도 개발)를 만들었다.</p>
<p>2일 차에서는 페이지 구성과 페이지 내용에 대해 이야기했다면 3일 차에서 각 페이지별로 사용자의 시나리오를 작성했다. 
생각보다 과정이 오래걸려서 다들 2시까지 남아있었다. </p>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/c1e90d94-1a3a-49d0-b9ea-27e74da6f608/image.png" alt=""></p>
<p>다들 피곤한데 늦게까지 남아서 열정이 대단했다.</p>
<p>나는 드래그앤드롭 기능을 구현하고 싶기도 했고, 가장 어려워보이는 부분인 사진 꾸미기 페이지를 선택했다!</p>
<p>그렇게 각자 파트를 정하고 끝냈다.</p>
<h1 id="45일-차">4~5일 차</h1>
<p>이제 본격적으로 개발을 시작하는 날이다.</p>
<p>다른 프론트엔드 개발자 중에 경력이 있는 사람이 나랑 키유(PL)밖에 없어서 의도치않게 내가 리드하게 되었다. 그래서 디렉토리 구조를 정하고 공통적으로 쓰는 컴포넌트들을 만들어서 방향을 잡아주었다.</p>
<p>그리고 블루(백엔드)와 이야기를 하면서 서로 디자인에 대한 개선점이 필요하다고 생각한 것을 알게되었다. 근데 일개 개발자(?)가 디자인에 대해 첨언해도 되는지 둘이서 끙끙 앓다가 왕굴(디자이너)에게 정중하게 말씀드렸다. 그런데 왕굴이 굉장히 나이스하게 받아들여주셨다!! 감동 🥹 </p>
<p>다같이 이야기하면서 디자인을 개선해나가는데 우리가 원하던 대로 개선이 이루어져서 정말 기뻤다. </p>
<p>개발도 재밌지만 이렇게 프로젝트의 모든 과정에 참여하는 것도 정말 뜻깊은 일인 것 같다!</p>
<h1 id="6일-차">6일 차</h1>
<p>그래도 어찌저찌 완성해서 배포하기는 했는데 데모 버전을 발표하는 시간에 내 파트에서 오류가 발생한 것이다!! 😱</p>
<p>분명히 백엔드랑 새벽까지 열심히 테스트 했는데...</p>
<p>확인해보니 로컬 파일의 경우 꾸미고 저장하는 것은 잘 됐는데, db에 저장된 이미지를 불러와서 꾸미고 저장할 때 스티커가 붙여진 이미지가 아닌 <strong>스티커만 저장이 된 것</strong>이였다.</p>
<h3 id="내가-바란-것">내가 바란 것</h3>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/e6bd8c60-38a6-4d23-9501-efa02659be77/image.png" alt=""></p>
<h3 id="현실">현실</h3>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/8d95de95-602e-43e4-817e-70a307f6423c/image.png" alt=""></p>
<p><del>냅다 스티커만 저장이 돼...</del></p>
<p>왜 그런가 찾아보니까 html2canvas는 외부 이미지의 경우 <strong>CORS에러</strong>가 나서 이미지가 같이 저장이 안되는 것이였다.</p>
<p>전자의 경우만 테스트해보고 후자의 경우는 생각도 못했다..바보..</p>
<p>스프린트가 끝난 뒤에 문제를 해결하기 위해 Cloud Storage 버킷에 CORS 설정을 추가하고, html2canvas에서 <code>useCORS: true</code> 옵션을 설정해서 CORS 요청을 허용했더니 스티커와 이미지가 잘 저장되었다! 생각보다 다양한 곳에서 CORS에러가 나타나고 나를 <del>괴롭힌다..</del></p>
<p>그래도 이번 기회에 html2canvas도 써보고 CORS에러가 날 수 있다는 사실을 알게 되었으니.. 러키비키자나..🍀</p>
<h2 id="대박-정신없는-회고">대박 정신없는 회고</h2>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/0ea0bbb9-8dde-4f9c-b594-26e40fa85511/image.png" alt=""></p>
<h1 id="느낀점">느낀점</h1>
<p>5일밖에 안되는 짧은 기간이였지만, 그래서 더 몰입할 수 있어서 즐거운 시간이였다.</p>
<p>주어진 것을 그냥 개발하는 게 아니라 아이디어 선정부터 기획까지 모든 과정에 참여할 수 있어서 더 값진 경험이었다.</p>
<p>마지막에 내가 맡은 부분에 문제가 있어서 그 부분이 정말 아쉬웠다 🥲 그래서 회고하는 시간에 기어들어가는 목소리로 자신없이 얘기했더니 팀원분들이 괜찮다고 제일 어려운 부분 맡은거 아니냐고 격려해주셨다.. ㅠㅠ 정말 좋은 사람들..</p>
<p>테스트를 할 때 정말 다양한 상황에서 테스트를 해봐야겠구나 라는걸 느꼈다. 사용자의 입장에서 테스트를 했어야 했는데 내가 의도한 대로 동작하는지만 확인했다. </p>
<p>앞으로는 다양한 시나리오를 고려해서 테스트에 더 신경을 쓸 계획이다!</p>
<p>링크)
<a href="https://4cus.vercel.app/">https://4cus.vercel.app/</a>
지금은 백엔드 서버가 죽어서 동작은 안한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리눅스 기초]]></title>
            <link>https://velog.io/@developer-sora/%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@developer-sora/%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Sat, 20 Jul 2024 13:23:54 GMT</pubDate>
            <description><![CDATA[<h1 id="리눅스linux">리눅스(Linux)</h1>
<p>리눅스는 컴퓨터 운영체제 <strong>커널</strong>의 일종인 <strong>Linux 커널</strong>, 또는 Linux 커널을 사용하는 운영체제를 가리킨다.</p>
<blockquote>
<p><strong>커널</strong>?
운영 체제의 주요 구성 요소로 하드웨어와 프로세스를 이어주며 컴퓨터 자원을 관리한다.</p>
</blockquote>
<p>컴퓨터 역사상 가장 많은 참여자가 관여하고 있으며, 대표적인 오픈 소스 프로젝트이다. 많은 컴퓨터와 스마트폰, 임베디드 기기가 Linux로 작동한다.</p>
<h2 id="리눅스의-역사">리눅스의 역사</h2>
<p>리눅스는 리누스 토르발스(Linus Torvalds)에 의해 개발되었다.</p>
<p>그는 운영체제인 유닉스(Unix)의 교육용 버전인 미닉스(Minix)의 불완전한 부분을 보완하고자 커널에 여러가지 기능을 추가했고 이것이 운영체제에 가까워지면서 리눅스로 릴리스하게 되었다. (Linus + xNix = Linux)</p>
<p>처음에는 리눅스를 빌드하는데 미닉스가 필요했지만, 미닉스가 부팅이 안되게 되면서 리눅스에서 리눅스를 컴파일할 수 있도록 하였다.</p>
<h3 id="gnu와의-통합">GNU와의 통합</h3>
<p>초기 Linux는 기능이 불완전한 운영체제였는데, 자체 커널 개발에 난항을 겪고 있던 GNU 프로젝트가 Linux 커널에 관심을 가졌고 GNU 유틸리티와 Linux커널이 결합하면서 완전한 운영체제가 탄생하게 되었다.</p>
<h3 id="gnu">GNU</h3>
<p>GNU(Gnu is Not Unix) 프로젝트는 컴퓨터 기업들이 유닉스를 상용화해서 사용료를 요구하는 것에 반발해서 시작되었다. GNU 프로젝트는 누구나 공개된 소스를 수정하고 배포할 수 있게 했으며, 운영체제에 필요한 많은 프로그램(라이브러리, 컴파일러, 텍스트 편집기, 유닉스 쉘 등)을 만들어냈다.</p>
<h2 id="배포판">배포판</h2>
<p>리눅스는 리눅스 커널에 기반을 둔 운영체제를 말하는데, 리눅스 커널을 사용하는 수많은 배포판이 있다. </p>
<p>따라서 우리가 실제로 접하고 사용하는 리눅스는 리눅스 배포판을 뜻한다.</p>
<p>대표적인 배포판으로 데비안, 페도라, 우분투, 레드햇 등이 있다.</p>
<p>그 중에서 우분투는 현재 개인용 데스크톱 환경에서 가장 인기있는 리눅스 배포판으로 사용자 친화적이고 안정적인 운영체제를 제공한다.</p>
<h2 id="리눅스와-유닉스의-차이">리눅스와 유닉스의 차이</h2>
<ul>
<li>Unix - 독점 운영 체제, <strong>유료</strong>, 인프라 등에 사용됨</li>
<li>Linux - 오픈 소스, <strong>무료,</strong> 스마트폰 혹은 PC 등 다양한 컴퓨터 하드웨어에 설치 가능</li>
</ul>
<p>리눅스와 유닉스의 가장 큰 차이점은 오픈소스냐 아니냐인 것 같다. </p>
<p>리눅스가 오픈소스이기 때문에 커뮤니티가 활성화 되어있고, 다양한 배포판이 있어 유닉스보다 더 큰 사용자 기반을 갖고 있다.</p>
<h2 id="리눅스-사용-이유">리눅스 사용 이유</h2>
<p>그래서 리눅스를 왜 사용하는가 🤔</p>
<ol>
<li>일단 무료다<ul>
<li>웹서버로 리눅스를 사용하는 가장 큰 이유이다. 웹서버는 하루종일 돌아가기때문에 무료인 것이 중요하다.</li>
</ul>
</li>
<li>높은 보안성<ul>
<li>Windows보다 더 안전한 환경에서 개발할 수 있다. Linux 배포자가 분산되어 있고 악성코드가 주로 Windows를 타겟으로 하기 때문에 상대적으로 멀웨어로부터 자유롭다.</li>
</ul>
</li>
<li>안정된 구조<ul>
<li>리눅스 서버 가동시간은 매우 높고 가용성은 약 99.9%라고 한다.</li>
<li>전세계 기업 및 개발자가 사용하고 있고, 오픈소스 운영체제이기 때문에 버그가 생기더라도 금방 수정되고 Fix된다.</li>
<li>리눅스의 업데이트는 시스템을 재부팅하지 않고도 가능하다.</li>
</ul>
</li>
<li>개발 편리성<ul>
<li>GUI 상에서 해결할 수 있는 문제를 CLI 셸을 통해서 처리할 수 있다.</li>
<li>Window와 macOS의 경우 GUI 환경에 시스템에 고정으로 포함되어 있어 분리할 수 없다. 하지만 Linux에서는 커널과 GUI가 분리되어 필요에 따라 선택해서 사용할 수 있다.</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 전력망을 둘로 나누기 (Javascript)]]></title>
            <link>https://velog.io/@developer-sora/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%A0%84%EB%A0%A5%EB%A7%9D%EC%9D%84-%EB%91%98%EB%A1%9C-%EB%82%98%EB%88%84%EA%B8%B0-javascript</link>
            <guid>https://velog.io/@developer-sora/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%A0%84%EB%A0%A5%EB%A7%9D%EC%9D%84-%EB%91%98%EB%A1%9C-%EB%82%98%EB%88%84%EA%B8%B0-javascript</guid>
            <pubDate>Sun, 30 Jun 2024 09:24:16 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/86971">https://school.programmers.co.kr/learn/courses/30/lessons/86971</a></p>
<h1 id="문제">문제</h1>
<p>n개의 송전탑이 전선을 통해 하나의 트리 형태로 연결되어 있습니다. 당신은 이 전선들 중 하나를 끊어서 현재의 전력망 네트워크를 2개로 분할하려고 합니다. 이때, 두 전력망이 갖게 되는 송전탑의 개수를 최대한 비슷하게 맞추고자 합니다.</p>
<p>송전탑의 개수 n, 그리고 전선 정보 wires가 매개변수로 주어집니다. 전선들 중 하나를 끊어서 송전탑 개수가 가능한 비슷하도록 두 전력망으로 나누었을 때, 두 전력망이 가지고 있는 송전탑 개수의 차이(절대값)를 return 하도록 solution 함수를 완성해주세요.</p>
<h3 id="제한사항">제한사항</h3>
<p>n은 2 이상 100 이하인 자연수입니다.
wires는 길이가 n-1인 정수형 2차원 배열입니다.
wires의 각 원소는 [v1, v2] 2개의 자연수로 이루어져 있으며, 이는 전력망의 v1번 송전탑과 v2번 송전탑이 전선으로 연결되어 있다는 것을 의미합니다.
1 ≤ v1 &lt; v2 ≤ n 입니다.
전력망 네트워크가 하나의 트리 형태가 아닌 경우는 입력으로 주어지지 않습니다.</p>
<h3 id="입출력-예시">입출력 예시</h3>
<table>
<thead>
<tr>
<th>n</th>
<th>wires</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>9</td>
<td>[[1,3],[2,3],[3,4],[4,5],[4,6],[4,7],[7,8],[7,9]]</td>
<td>3</td>
</tr>
<tr>
<td>4</td>
<td>[[1,2],[2,3],[3,4]]</td>
<td>0</td>
</tr>
<tr>
<td>7</td>
<td>[[1,2],[2,7],[3,7],[3,4],[4,5],[6,7]]</td>
<td>1</td>
</tr>
</tbody></table>
<h4 id="입출력-예-설명">입출력 예 설명</h4>
<p>입출력 예 #1</p>
<ul>
<li>다음 그림은 주어진 입력을 해결하는 방법 중 하나를 나타낸 것입니다.
<img src="https://velog.velcdn.com/images/developer-sora/post/62367bf2-1344-4516-a0ac-4f30a271bfb2/image.png" alt=""></li>
<li>4번과 7번을 연결하는 전선을 끊으면 두 전력망은 각 6개와 3개의 송전탑을 가지며, 이보다 더 비슷한 개수로 전력망을 나눌 수 없습니다.</li>
<li>또 다른 방법으로는 3번과 4번을 연결하는 전선을 끊어도 최선의 정답을 도출할 수 있습니다.</li>
</ul>
<h1 id="풀이">풀이</h1>
<h2 id="접근-방식">접근 방식</h2>
<ol>
<li>전선 정보를 저장할 데이터 구조를 정하고 저장한다.<ul>
<li>배열로 정한 이유는 구현하기 간단하고 순회하기 편하기 때문에</li>
<li>연결된 모든 node를 표현해준다. 
ex) 1→3, 2→3, 3→1, 2, 4 ... = [[], [3], [3], [1,2,4], ...]<pre><code class="language-javascript">const graph = Array.from({ length: n + 1 }, () =&gt; []);
for (const [v1, v2] of wires) {
  graph[v1].push(v2);
  graph[v2].push(v1);
}</code></pre>
</li>
</ul>
</li>
</ol>
<ol start="2">
<li><p>이어져있는 전선의 수를 count하는 함수를 구현한다.</p>
<ul>
<li>시작 노드와 제외할 노드를 인자로 받는다.</li>
<li>예를 들어 1→3인 경우 1번 송전탑과 3번 송전탑이 연결되어있다는 의미로 3→1과 동일한 경우이다. 따라서 중복 카운트를 방지하기 위해 visited 배열을 만들어서 방문 여부를 체크해준다.</li>
<li>시작 노드와 연결되어 있는 노드를 순회하면서 제외할 노드가 아니면서 방문하지 않은 노드인 경우 방문처리를 하고, 큐에 넣는다.</li>
<li>연결된 모든 노드를 순회한 후, 큐에 남아 있는 첫 번째 노드를 다시 순회하게 된다. 이는 해당 노드가 시작 노드와 연결되었다는 의미이므로 count를 증가시켜준다.</li>
<li>해당 큐가 빌 때 까지 반복한다.(모든 노드 방문)<pre><code class="language-javascript">const bfs = (start, except) =&gt; {
let count = 0;
const queue = [start];
let visited = Array.from({ length: n + 1 }, () =&gt; false);
visited[start] = true;
while (queue.length !== 0) {
const index = queue.shift();
graph[index].forEach((v) =&gt; {
  if (v !== except &amp;&amp; !visited[v]) {
    visited[v] = true;
    queue.push(v);
  }
});
count++;
}
return count;
};</code></pre>
</li>
</ul>
</li>
<li><p>전선을 전부 순회하면서 나눠진 전력망이 가진 송전탑의 차이가 가장 적은 수를 return해준다.</p>
<pre><code class="language-javascript">   wires.forEach(([v1, v2]) =&gt; {
     answer = Math.min(answer, Math.abs(bfs(v1, v2) -     bfs(v2, v1)));
   });</code></pre>
</li>
</ol>
<h2 id="전체-코드">전체 코드</h2>
<pre><code class="language-javascript">function solution(n, wires) {
  const graph = Array.from({ length: n + 1 }, () =&gt; []);

  let answer = Number.MAX_SAFE_INTEGER;

  for (const [v1, v2] of wires) {
    graph[v1].push(v2);
    graph[v2].push(v1);
  }

  const bfs = (start, except) =&gt; {
    let count = 0;
    const queue = [start];
    let visited = Array.from({ length: n + 1 }, () =&gt; false);
    visited[start] = true;
    while (queue.length !== 0) {
      const index = queue.shift();
      graph[index].forEach((v) =&gt; {
        if (v !== except &amp;&amp; !visited[v]) {
          visited[v] = true;
          queue.push(v);
        }
      });
      count++;
    }
    return count;
  };

  wires.forEach(([v1, v2]) =&gt; {
    answer = Math.min(answer, Math.abs(bfs(v1, v2) - bfs(v2, v1)));
  });

  return answer;
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준/1916/node.js] 최소비용 구하기]]></title>
            <link>https://velog.io/@developer-sora/%EB%B0%B1%EC%A4%801916node.js-%EC%B5%9C%EC%86%8C%EB%B9%84%EC%9A%A9-%EA%B5%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@developer-sora/%EB%B0%B1%EC%A4%801916node.js-%EC%B5%9C%EC%86%8C%EB%B9%84%EC%9A%A9-%EA%B5%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 21 May 2024 09:50:07 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/193b4ca0-34b6-4cbc-b0c3-12612639517f/image.png" alt=""></p>
<h2 id="풀이">풀이</h2>
<p>한 노드(출발 도시)에서 도착 노드(도착 도시)까지 가는 최단 경로를 구하고 싶을 때는 데이크스트라 알고리즘을 사용하면 된다.</p>
<h2 id="데이크스트라dijkstra-알고리즘">데이크스트라(Dijkstra) 알고리즘</h2>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/a2c036e0-c86e-45a3-9863-a1b961de1156/image.gif" alt=""></p>
<p><a href="https://ko.wikipedia.org/wiki/%EB%8D%B0%EC%9D%B4%ED%81%AC%EC%8A%A4%ED%8A%B8%EB%9D%BC_%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98">데이크스트라 알고리즘</a>은 그래프에서 꼭짓점 간의 최단 경로를 찾는 알고리즘을 말한다.</p>
<h3 id="데이크스트라dijkstra-vs-플로이드-워셜floyd-warshall">데이크스트라(Dijkstra) vs 플로이드 워셜(Floyd Warshall)</h3>
<p>Dijkstra 알고리즘은 <strong>하나의 정점</strong>에서 출발했을 때 다른 모든 정점으로의 최단 경로를 구하는 알고리즘이고,
Floyd Warshall 알고리즘은 <strong>모든 정점</strong>에서 다른 모든 정점으로의 최단 경로를 구하는 알고리즘이다.</p>
<p>예를 들어, GPS 시스템에서 특정 출발 위치에서 모든 목적지로의 최단 경로를 찾는 경우에는 <strong>Dijkstra 알고리즘</strong>을 사용하고, 도시 간의 최단 거리를 찾는 경우나 네트워크에서 모든 호스트 간의 최단 경로를 찾을 때는 <strong>Floyd Warshall 알고리즘</strong>을 사용할 수 있다.</p>
<h3 id="알고리즘">알고리즘</h3>
<ol>
<li><p>출발점으로부터의 최단거리를 저장할 배열 <code>d[v]</code>(v는 정점의 총 개수)를 만들고, 출발점에는 0을, 출발 점을 제외한 다른 노드들에는 INF를 넣는다.</p>
</li>
<li><p>정점 A에서 갈 수 있는 임의의 노드 B에 대해 <code>d[A] + P[A][B]</code>(A와 B사이의 최단 거리)와 <code>d[B]</code>의 값을 비교한다. (INF와 비교할 경우 무조건 전자가 작다)</p>
</li>
<li><p>만약 전자가 값이 작다면(더 짧은 경로라면), <code>d[B]</code>의 값을 <code>d[A] + P[A][B]</code>값으로 갱신시킨다.</p>
</li>
<li><p>A의 모든 이웃 노드 B에 대해 이 작업을 수행한다.</p>
</li>
<li><p>A는 모든 노드를 방문했으니 <code>방문 완료</code> 처리하고 더이상 A를 사용하지 않는다.</p>
</li>
<li><p><code>미방문</code> 상태인 노드들 중, 출발점으로부터의 거리가 제일 짧은 노드 하나를 골라서 그 노드를 A에 저장한다.</p>
</li>
</ol>
<ol start="7">
<li><p>도착 노드가 <code>방문 완료</code> 상태가 되거나, 혹은 미방문 상태의 노드를 선택할 수 없을 때 까지, 2~7의 과정을 반복한다.</p>
</li>
<li><p>이 작업을 마친 뒤, 도착 노드에 저장된 값이 바로 A로부터의 최단 거리이다.</p>
</li>
</ol>
<h3 id="순차-탐색-구현">순차 탐색 구현</h3>
<p>6번 단계(미방문 노드 중 거리 값이 가장 작은 노드 찾기)에서 순차 탐색을 하는 경우 노드의 개수 만큼 순차 탐색을 수행해야 하기 때문에 <strong>O(N²)</strong>의 시간이 걸린다.</p>
<p><strong>입력 받는 부분</strong></p>
<pre><code class="language-javascript">// 입력 받기
let fs = require(&quot;fs&quot;);
let [N, M, ...input] = fs
  .readFileSync(&quot;./Baekjoon/input.txt&quot;)
  .toString()
  .trim()
  .split(&quot;\n&quot;);

N = +N;  // 도시의 개수
M = +M;  // 버스의 개수

// 출발 도시 start, 도착 도시 end
let [start, end] = input.pop().split(&quot; &quot;).map(Number);

// 버스의 정보
input = input.map((v) =&gt; v.split(&quot; &quot;).map(Number));

// 버스의 정보를 저장할 배열
let graph = Array.from({ length: N + 1 }, () =&gt; []);

// 출발도시 a의 도착 도시 b, 비용 c를 객체 형태로 저장
for (let [a, b, c] of input) {
  graph[a].push({ node: b, cost: c });
}

// 최단거리를 저장할 배열
let dist = Array.from({ length: N + 1 }).fill(Infinity);
// 방문여부를 저장할 배열
let visited = Array.from({ length: N + 1 }).fill(false);</code></pre>
<p><strong>구현 부분</strong></p>
<pre><code class="language-javascript">/* 아직 방문하지 않은 노드 중 
   가장 거리값이 작은 노드 반환 */
function findSmallestNode() {
  let min_dist = Infinity;
  let min_node = 0;
  for (let i = 1; i &lt;= N; i++) {
    if (!visited[i] &amp;&amp; dist[i] &lt; min_dist) {
      min_dist = dist[i];
      min_node = i;
    }
  }
  return min_node;
}

// 데이크스트라
function dijkstra() {
  dist[start] = 0;
  visited[start] = true;

  // 출발 도시에서 인접 도시까지의 최단거리 계산
  graph[start].forEach((next) =&gt; {
    dist[next.node] = Math.min(dist[next.node], next.cost);
  });

  for (let i = 1; i &lt;= N; i++) {
    let now_node = findSmallestNode();
    visited[now_node] = true;

    graph[now_node].forEach((next) =&gt; {
      const acc = dist[now_node] + next.cost;
      if (acc &lt; dist[next.node] &amp;&amp; !visited[next.node]) {
        dist[next.node] = acc;
      }
    });
  }
}

dijkstra();</code></pre>
<h4 id="처음에-헤맸던-점">처음에 헤맸던 점</h4>
<ol>
<li><p>버스 정보를 받는 부분에서 편도인데 왕복이 가능하다고 생각했다. A -&gt; B인 경우 B -&gt; A도 성립한다고 생각함 </p>
<pre><code class="language-javascript"> for (let [a, b, c] of input) {
       graph[a].push({ node: b, cost: c });
   //graph[b].push({ node: a, cost: c }); 이 부분이 필요 없다!
 }</code></pre>
</li>
<li><p>한 도시에서 다른 도시로 가는 버스편이 여러개 존재할 수 있음을 간과했다.</p>
<pre><code class="language-javascript">graph[start].forEach((next) =&gt; {
  // 여러 버스편 중 가장 비용이 적은 버스편을 채택
     dist[next.node] = Math.min(dist[next.node], next.cost);

});</code></pre>
</li>
</ol>
<p>데익스트라 알고리즘은 순차 탐색을 사용하는 경우 탐색 시간이 오래걸리기 때문에 보통 우선순위 큐를 사용한다고 한다. 지금은 순차 탐색으로 통과했지만 순차 탐색으로 안되는 경우 javascript로 우선순위 큐를 구현하는 방법을 알아봐야겠다. 🥹</p>
<blockquote>
<p>출처: <a href="https://ko.wikipedia.org/wiki/%EB%8D%B0%EC%9D%B4%ED%81%AC%EC%8A%A4%ED%8A%B8%EB%9D%BC_%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98">https://ko.wikipedia.org/wiki/%EB%8D%B0%EC%9D%B4%ED%81%AC%EC%8A%A4%ED%8A%B8%EB%9D%BC_%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</a>
<a href="https://namu.wiki/w/%EB%8B%A4%EC%9D%B5%EC%8A%A4%ED%8A%B8%EB%9D%BC%20%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98">https://namu.wiki/w/%EB%8B%A4%EC%9D%B5%EC%8A%A4%ED%8A%B8%EB%9D%BC%20%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준/1629/node.js] 곱셈]]></title>
            <link>https://velog.io/@developer-sora/%EB%B0%B1%EC%A4%801629node.js-%EA%B3%B1%EC%85%88</link>
            <guid>https://velog.io/@developer-sora/%EB%B0%B1%EC%A4%801629node.js-%EA%B3%B1%EC%85%88</guid>
            <pubDate>Sat, 11 May 2024 10:37:43 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p><a href="https://www.acmicpc.net/problem/1629">https://www.acmicpc.net/problem/1629</a></p>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/9f53adb4-46b3-4790-93a5-cf6522c022e2/image.png" alt=""></p>
<h2 id="풀이">풀이</h2>
<p>처음에 냅다 A를 B번 곱하는 식으로 풀었는데 시간초과가 나와서(<del>실버 1인데 그렇게 간단할 리가 없지😭</del>) 무슨 알고리즘을 사용하는 건가 싶었다. 하긴 숫자 범위가 1 ~ 2,147,483,647까진데 O(n)으로 구하면 시간초과가 날 법 하다. 그래서 알고리즘 분류를 봤더니 분할 정복을 이용한 거듭제곱이라고 나와있었다. 분할 정복 알고리즘을 처음 접해서 정리해보기로 했다!</p>
<h3 id="분할-정복-알고리즘">분할 정복 알고리즘</h3>
<blockquote>
<p><strong>분할 정복 알고리즘</strong>이란 그대로 해결할 수 없는 문제를 작은 문제로 분할하여 문제를 해결하는 방법이나 알고리즘을 말한다.</p>
</blockquote>
<p>예를 들어서 2¹¹ 을 구하는 경우, 2를 11번 곱하는 방식으로 구할 수도 있지만(11번 계산) </p>
<pre><code>2¹¹ = 2⁵ * 2⁵ * 2
2⁵ = 2² * 2² * 2
2² = 2¹ * 2¹</code></pre><p>이렇게 세 번의 연산만으로도 구할 수 있다!</p>
<pre><code>Aⁿ = Aⁿᐟ² * Aⁿᐟ²                (n이 짝수인 경우)
Aⁿ = A⁽ⁿ⁻¹⁾ᐟ² * A⁽ⁿ⁻¹⁾ᐟ² * A    (n이 홀수인 경우)</code></pre><p>즉 분할 정복 거듭제곱은 이런 식으로 만들어진다.</p>
<h3 id="코드-작성하기">코드 작성하기</h3>
<pre><code class="language-javascript">function power(base, exponent) {
  if (exponent === 1) {    
    return base;            
  } else if (exponent % 2 === 0) {
    let byTwo = power(base, exponent / 2);
    return byTwo * byTwo;
  } else {
    let byTwoPlusOne = power(base, (exponent - 1) / 2);
    return byTwoPlusOne * byTwoPlusOne * base;
  }
}</code></pre>
<p>하지만 위의 문제의 내용처럼 구하려는 수가 매우 커질 수 있으므로 보통 어떤 수(예를 들면 N)로 나눈 나머지를 구한다.</p>
<pre><code class="language-javascript">function power(base, exponent) {
  if (exponent === 1) {
    return base % N;
  } else if (exponent % 2 === 0) {
    let byTwo = power(base, exponent / 2) % N;
    return (byTwo * byTwo) % N;
  } else {
    let byTwoPlusOne = power(base, (exponent - 1) / 2) % N;
    return (((byTwoPlusOne * byTwoPlusOne) % N) * base) % N;
  }
}</code></pre>
<p>모듈로 연산에 의해 <code>(A * B) % C =  (A % C * B % C) % C</code>가 성립한다.<br>따라서 수가 너무 커질 수 있기 때문에 계산할 때 마다 나머지로 계산해준다!</p>
<p>그래서 위의 코드를 이용해서 정답을 제출했는데.. 자꾸 1%에서 틀리는 것이였다 ㅠㅠ 아마도 수의 범위가 너무 커서 자바스크립트의 출력 범위를 넘어서는 것 같았다. 그래서 BigInt를 사용하기로 했다.</p>
<h2 id="정답-코드">정답 코드</h2>
<pre><code class="language-javascript">let fs = require(&quot;fs&quot;);
let [A, B, C] = fs
  .readFileSync(&quot;/dev/stdin&quot;)
  .toString()
  .split(&quot; &quot;)
  .map(BigInt);   // BigInt로 형변환 해준다.

function power(base, exponent) {
  if (exponent === 1n) {   // BigInt라서 BigInt로 비교
    return base % C;
  } else if (exponent % 2n === 0n) { // BigInt로 연산
    let byTwo = power(base, exponent / 2n) % C;
    return (byTwo * byTwo) % C;
  } else {
    let byTwoPlusOne = power(base, (exponent - 1n) / 2n) % C;
    return (((byTwoPlusOne * byTwoPlusOne) % C) * base) % C;
  }
}

console.log(power(A, B).toString()); 
// 숫자에 붙은 n을 떼줘야 해서 toString()을 해준다!</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준/7576/node.js] 토마토]]></title>
            <link>https://velog.io/@developer-sora/%EB%B0%B1%EC%A4%807576node.js-%ED%86%A0%EB%A7%88%ED%86%A0</link>
            <guid>https://velog.io/@developer-sora/%EB%B0%B1%EC%A4%807576node.js-%ED%86%A0%EB%A7%88%ED%86%A0</guid>
            <pubDate>Mon, 06 May 2024 12:29:21 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/developer-sora/post/77c8ab19-a2c2-4948-b442-0d0824636b00/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/1b0207e7-cad3-4f00-abc4-eca18bba97ca/image.png" alt=""></p>
<p>위 예제에서 익은 토마토의 위치가 [3,5]이고, 여기서 하루가 지나면 인접한 위치인 [2,5],[3,4]에 있는 토마토가 익는다.</p>
<p>따라서</p>
<blockquote>
<p>0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 <strong>1</strong></p>
</blockquote>
<p>[3,5]</p>
<blockquote>
<p>0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 <strong>1</strong>
0 0 0 0 <strong>1</strong> 1</p>
</blockquote>
<p>[3,5] -&gt; [2,5], [3,4]로 이동,</p>
<blockquote>
<p>0 0 0 0 0 0
0 0 0 0 0 <strong>1</strong>
0 0 0 0 <strong>1</strong> 1
0 0 0 <strong>1</strong> 1 1</p>
</blockquote>
<p>[2,5] -&gt; [1,5], [2,4]로 이동,
[3,4] -&gt; [3,3]으로 이동한다.</p>
<p>따라서 먼저 들어간 데이터가 먼저 나가는 queue 자료구조를 이용한 BFS를 쓰면 된다는 것을 알게되었다!</p>
<p>처음에 shift() 메서드를 사용했었는데 시간초과가 나서 질문 게시판을 봤더니
<img src="https://velog.velcdn.com/images/developer-sora/post/2ac7bdcd-366f-400c-b125-e800bd7b3e11/image.png" alt=""></p>
<p>이런식으로 구현하는 방법이 있다고 해서 2번째 방법을 쓰기로 했다!</p>
<blockquote>
<p>shift() 메서드는 맨 앞에 있는 값을 제거한 후에 기존에 배열에 있던 모든 요소를 한 자리씩 왼쪽으로 이동시켜야 하기 때문에 시간 복잡도가 <strong>O(n)</strong> 이라고 한다.</p>
</blockquote>
<p>shift 메서드가 생각보다 오래 걸리는 메서드였다... 
무지성으로 사용하고 있던 나 반성 🫠</p>
<h2 id="👩💻-전체-코드">👩‍💻 전체 코드</h2>
<pre><code class="language-javascript">// 인풋 값 입력 받는 코드
let fs = require(&quot;fs&quot;);
let input = fs.readFileSync(&quot;/dev/stdin&quot;).toString().split(&quot;\n&quot;);

let [M, N] = input.shift().split(&quot; &quot;).map(Number);

let arr = [];

input.forEach((v) =&gt; arr.push(v.split(&quot; &quot;).map(Number)));

// 상하좌우 방향 
const directionX = [-1, 0, 1, 0];
const directionY = [0, 1, 0, -1];

const queue = [];

let date = 0;

let pointer = 0;

function bfs() {
  while (queue.length !== 0) {
    if (pointer + 1 &gt; queue.length) {
      return;
    }  // pointer가 queue의 길이를 넘어가면 return
    let { x: curX, y: curY, date: curDate } = queue[pointer++];
    date = curDate; // queue에 있는 date로 갱신
    for (let i = 0; i &lt; 4; i++) {
      let nextX = curX + directionX[i];
      let nextY = curY + directionY[i];
      if (nextX &lt; 0 || nextY &lt; 0 || nextX &gt;= N || nextY &gt;= M) continue;
      if (arr[nextX][nextY] === 0) {
        arr[nextX][nextY] = 1;  // 익은 토마토로 바꿔줌
        queue.push({ x: nextX, y: nextY, date: curDate + 1 }); 
        // queue에 다음 좌표와 마지막 queue의 date에 하루를 추가해서 넣어줌
      }
    }
  }
}

for (let i = 0; i &lt; N; i++) {
  for (let j = 0; j &lt; M; j++) {
    if (arr[i][j] &lt;= 0) continue;
    queue.push({ x: i, y: j, date: 0 });
  }
} // 제일 처음에 익은 토마토들을 queue에 넣어줌

for (let i = 0; i &lt; N; i++) {
  for (let j = 0; j &lt; M; j++) {
    bfs();
  }
}

for (let i = 0; i &lt; N; i++) {
  for (let j = 0; j &lt; M; j++) {
    if (arr[i][j] === 0) {   // 익지 않은 토마토가 있다면
      console.log(-1);       // -1 출력
      process.exit();
    }
  }
}

console.log(date);
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준/1260/node.js]DFS와 BFS ]]></title>
            <link>https://velog.io/@developer-sora/%EB%B0%B1%EC%A4%801260node.jsDFS%EC%99%80-BFS</link>
            <guid>https://velog.io/@developer-sora/%EB%B0%B1%EC%A4%801260node.jsDFS%EC%99%80-BFS</guid>
            <pubDate>Sun, 14 Jan 2024 08:52:07 GMT</pubDate>
            <description><![CDATA[<p>DFS와 BFS의 정석같은 문제이다.
DFS랑 BFS 문제는 몇 번 풀어봤는데 풀 때마다 헷갈림..
익숙해질 만큼 풀자!</p>
<h3 id="dfs와-bfs">DFS와 BFS</h3>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/f2ade737-4149-4832-b0d8-634dc00ed98d/image.png" alt=""></p>
<p>쉽게 얘기하면 DFS는 아래로 계속 내려가고 BFS는 옆으로 계속 옮겨간다고 생각하면 된다.
그래서 DFS는 재귀나 stack을 사용하고
BFS는 queue를 사용해서 푼다.</p>
<p>위 문제는 모든 노드에 방문하는 문제라서 인접리스트를 사용해서 풀었다.</p>
<p>인접행렬을 사용해서 풀 경우 노드에 연결되지 않은 부분도 확인하기 때문에 시간복잡도가 더 증가한다.</p>
<h4 id="예시">예시</h4>
<p>예제가 아래와 같은 경우,</p>
<p>5 5 3
5 4
5 2
1 2
3 4
3 1</p>
<p>인접리스트를</p>
<p>1 [2, 3]
2 [1, 5]
3 [1, 4]
4 [3, 5]
5 [2, 4]</p>
<p>이렇게 만들 수 있다. (오름차순으로 정렬함)</p>
<p><code>DFS</code>로 탐색하는 경우 </p>
<ol>
<li>시작점 <code>3</code>에서 연결된 정점 중 가장 작은 정점 번호인 <code>1</code>을 방문하고 </li>
<li><code>1</code>에서 연결된 가장 작은 정점 번호인 <code>2</code>를 방문, </li>
<li><code>2</code>에서 연결된 가장 작은 정점 번호가 <code>1</code>이지만 <code>1</code>은 이미 방문했기 때문에 통과하고 <code>5</code>를 방문, </li>
<li><code>5</code>에서 연결된 가장 작은 정점번호인 <code>2</code>가 이미 방문했기 때문에 <code>4</code>를 방문하고 끝난다.(모든 정점 순회)</li>
</ol>
<p>따라서 <code>3 -&gt; 1 -&gt; 2 -&gt; 5 -&gt; 4</code> 가 나온다.</p>
<p><code>BFS</code>로 탐색하는 경우 </p>
<ol>
<li>시작점 <code>3</code>에서 연결된 정점중 가장 작은 정점 번호인 <code>1</code>을 방문하고, 그 다음 정점 번호인 <code>4</code>를 방문, 그리고 <code>3</code>에서 연결된 정점이 더이상 없으므로 다음으로 넘어간다.</li>
<li>시작점과 첫번째로 연결된 정점 <code>1</code>과 연결된 <code>2</code>와 <code>3</code>을 방문하는데 <code>3</code>은 이미 방문했으므로 넘어간다. 그리고 <code>1</code>에서 연결된 정점이 없으므로 다음으로 넘어간다.</li>
<li>시작점과 두번째로 연결된 정점 <code>4</code>와 연결된 <code>3</code>과 <code>5</code>를 방문하는데 <code>3</code>은 이미 방문했으므로 넘어간다. 그리고 모든 정점을 방문했으므로 종료한다.</li>
</ol>
<p>따라서 <code>3 -&gt; 1 -&gt; 4 -&gt; 2 -&gt; 5</code> 가 된다.</p>
<h3 id="전체-코드">전체 코드</h3>
<pre><code class="language-javascript">// 인풋값 입력받기
let fs = require(&quot;fs&quot;);
let input = fs
  .readFileSync(&quot;/dev/stdin&quot;)
  .toString()
  .trim()
  .split(&quot;\n&quot;);

const [N, M, V] = input.shift().split(&quot; &quot;).map(Number);

const line = input.map((v) =&gt; v.split(&quot; &quot;).map(Number));

const ansDfs = [];

const ansBfs = [];

const graph = Array.from({ length: N + 1 }, () =&gt; []);

let visited = Array.from({ length: N + 1 }, () =&gt; 0);

const queue = [];

// 인접리스트 만들기
for (let [from, to] of line) {
  graph[from].push(to);
  graph[to].push(from); 
 // [1,2] 일 경우 1[2] 2[1] 두 가지 경우가 나온다.
}

for (let i = 1; i &lt; graph.length; i++) {
  graph[i].sort((a, b) =&gt; a - b); 
 // 정점 번호가 작은 것을 먼저 방문하기 때문에 오름차순으로 정렬한다.
}

function dfs(cnt) {
  if (ansDfs.length === N) return; 
  // 최대 방문 정점 수는 최대 정점 번호보다 같거나 적어야 한다.
  ansDfs.push(cnt);
  visited[cnt] = 1;
  for (let next of graph[cnt]) {
    if (!visited[next]) {
      visited[next] = 1;
      dfs(next);
    }
  }
}

dfs(V);

visited = visited.map(() =&gt; 0);
// 방문한 정점 배열 초기화

function bfs() {
  queue.push(V);
  visited[V] = 1;
  while (queue.length !== 0) {
    const now = queue.shift();
    ansBfs.push(now);
    for (let next of graph[now]) {
      if (!visited[next]) {
        queue.push(next);
        visited[next] = 1;
      }
    }
  }
}

bfs();

console.log(ansDfs.join(&quot; &quot;));
console.log(ansBfs.join(&quot; &quot;));
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Native에서 토글 버튼 만들기]]></title>
            <link>https://velog.io/@developer-sora/React-Native%EC%97%90%EC%84%9C-%ED%86%A0%EA%B8%80-%EB%B2%84%ED%8A%BC-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@developer-sora/React-Native%EC%97%90%EC%84%9C-%ED%86%A0%EA%B8%80-%EB%B2%84%ED%8A%BC-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Wed, 13 Dec 2023 06:50:11 GMT</pubDate>
            <description><![CDATA[<p>토글을 Switch 컴포넌트로 만들었더니 iOS랑 Android의 기본 Switch 디자인이 다른거였다!</p>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/dc014d7e-6ac6-41d1-af50-799a99a155fc/image.png" alt=""></p>
<p>iOS는 내가 기대한 디자인대로 잘 나왔는데 Android는 저렇게 못생기게(?) 나왔다.</p>
<p>iOS에서만 테스트하거나 Android에서만 테스트 했으면 몰랐을 것들이 많다.. 항상 크로스 체크를 하자 ✅</p>
<p>아무튼 Switch 컴포넌트의 스타일을 바꿀 수 있나 찾아봤는데.. 결국 토글 버튼을 내가 직접 구현하는 방법밖에 없었다.</p>
<p>나는 typescript와 styled-components를 쓰고있다. 혹시 다른 스타일을 쓰고 계시다면 <del>챗GPT에게 물어봐서 수정하기를</del>.. </p>
<pre><code class="language-typescript">import React, {useEffect} from &#39;react&#39;;
import styled from &#39;styled-components/native&#39;;
import {Animated, Easing} from &#39;react-native&#39;;
import {colors} from &#39;@/styles/colors&#39;;

type Props = {
  onToggle: () =&gt; void;
  isOn: boolean;
};

const Toggle = ({onToggle, isOn}: Props) =&gt; {
  const [animatedValue] = useState(new Animated.Value(isOn ? 1 : 0));

  useEffect(() =&gt; {
    Animated.timing(animatedValue, {
      toValue: isOn ? 1 : 0,
      duration: 200,
      easing: Easing.linear,
      useNativeDriver: false,
    }).start();
  }, [isOn, animatedValue]);

  const translateX = animatedValue.interpolate({
    inputRange: [0, 1],
    outputRange: [1, 17],
  });

  const color = isOn ? colors.main : colors.backgroundGray;

  return (
    &lt;ToggleContainer onPress={onPress} color={color}&gt;
      &lt;ToggleWheel
        style={{
          transform: [{translateX}],
        }}
      /&gt;
    &lt;/ToggleContainer&gt;
  );
};

export default Toggle;

const ToggleContainer = styled.TouchableOpacity&lt;{color: string}&gt;`
  width: 36px;
  height: 20px;
  border-radius: 10px;
  justify-content: center;
  background-color: ${props =&gt; props.color};
`;

const ToggleWheel = styled(Animated.View)`
  width: 18px;
  height: 18px;
  background-color: white;
  border-radius: 99px;
`;
</code></pre>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/2abc1d34-1f61-4550-ac19-20605bde42df/image.gif" alt=""></p>
<p>잘 나온다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Gitlab 초기 환경 설정 🦊]]></title>
            <link>https://velog.io/@developer-sora/Gitlab-%EC%B4%88%EA%B8%B0-%ED%99%98%EA%B2%BD-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@developer-sora/Gitlab-%EC%B4%88%EA%B8%B0-%ED%99%98%EA%B2%BD-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Tue, 21 Nov 2023 09:28:32 GMT</pubDate>
            <description><![CDATA[<h2 id="로컬-pc에-git-설정">로컬 PC에 git 설정</h2>
<p><code>gitlab</code>이나 <code>github</code>나 똑같겠지~ 하고 똑같이 쓰다가 깃랩한테 호되게 혼났다.
<img src="https://velog.velcdn.com/images/developer-sora/post/1fe6b5c5-ad30-46f0-b5e0-c410cba7bd58/image.png" alt=""></p>
<p>깃허브처럼 <code>git init</code>하고 <code>git clone http:// 프로젝트 url</code>를 했는데 </p>
<pre><code>hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: &#39;git pull ...&#39;) before pushing again.
hint: See the &#39;Note about fast-forwards&#39; in &#39;git push --help&#39; for details.</code></pre><p>아래와 같은 경고문이 계속 나왔다.</p>
<p>그리고 어찌저찌 가져왔다 해도 브랜치 꼬이고 origin이 없다고 했다.😇</p>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/eb06eb06-3790-4781-8f2b-6f7727170fe0/image.png" alt=""></p>
<p>알고보니 <code>gitlab</code>은  원격 저장소라서 내 PC와 싱크를 맞춰야한다는 것이였다.</p>
<pre><code>git pull http:// 프로젝트 url</code></pre><p>이 과정을 빼먹어서 그렇게 고생했던 것이였다...</p>
<p>그러므로 re:zero부터 시작하는 gitlab 환경설정을 araboza.</p>
<hr/>

<ol>
<li><p>프로젝트를 진행할 폴더를 만들거나 프로젝트가 있는 폴더에 들어간다.</p>
</li>
<li><p>그리고 쉘을 열어 명령어를 이용해 유저의 정보를 설정해준다.</p>
</li>
</ol>
<pre><code>git config --global user.name &quot;이름&quot;    
git config --global user.email &quot;email&quot; </code></pre><ol start="3">
<li>git init으로 git 저장소를 생성한다.</li>
</ol>
<pre><code>git init</code></pre><ol start="4">
<li><p>보통 master 브랜치로 만들어져있으니 default 브랜치인 main 브랜치로 이동한다. (터미널이 경고함 default보다 main 쓰라고)</p>
<pre><code>git branch -m main</code></pre></li>
<li><p>git pull을 통해서 원격 저장소에 있는 것과 sync를 한다.
이걸 안하면 나중에 push가 안된다!!!</p>
<pre><code>git pull http://생성한 프로젝트 url</code></pre></li>
<li><p>remote로 원격 저장소와 연동시킨다.</p>
<pre><code>git remote add origin http://프로젝트 url</code></pre><p>기초 설정이 끝났으니 이 폴더에 프로젝트를 진행시키고 파일을 업로드 하면 된다.</p>
<pre><code>git add .
git commit -m &quot;커밋 메세지&quot;
git push --set-upstream origin main</code></pre></li>
</ol>
<blockquote>
<p>나를 구해주신 분
<a href="https://reakwon.tistory.com/178">https://reakwon.tistory.com/178</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Native Mac에서 시작하기 🍎]]></title>
            <link>https://velog.io/@developer-sora/React-Native-Mac%EC%97%90%EC%84%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@developer-sora/React-Native-Mac%EC%97%90%EC%84%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 15 Nov 2023 08:19:21 GMT</pubDate>
            <description><![CDATA[<h1 id="react-native-시작하기">React Native 시작하기</h1>
<h2 id="1homebrew-설치">1.Homebrew 설치</h2>
<hr/>
Homebrew는 Mac에서 필요한 패키지를 설치하고 관리하는 Mac용 패키지 관리자이다.


<p>명령어 한 줄로 프로그램을 설치/제거할 수 있기 때문에 Mac 사용자라면 꼭! 설치해야 한다.</p>
<h3 id="1homebrew-설치-1">1.Homebrew 설치</h3>
<p>터미널에 아래 명령어를 입력해서 Homebrew를 설치한다.</p>
<pre><code>/bin/bash -c &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)&quot;</code></pre><h3 id="2homebrew-버전-확인">2.Homebrew 버전 확인</h3>
<p>설치가 잘 되었다면 아래의 명령어를 통해 Homebrew의 버전을 확인할 수 있다.</p>
<pre><code>brew --version</code></pre><p><img src="https://velog.velcdn.com/images/developer-sora/post/c6954cef-66a6-4130-bb17-04b3b7d86e75/image.png" alt=""></p>
<blockquote>
<p>❗️ zsh: command not found: brew  에러가 뜨는 경우</p>
</blockquote>
<p>Homebrew가 <strong>/usr/local/...</strong> 가 아닌  <strong>/opt/homebrew/</strong> 에 설정되어 있어서 그렇다고 한다.</p>
<h4 id="해결-방법">해결 방법</h4>
<ol>
<li>.zshrc 파일을 vi로 열어준다.</li>
</ol>
<pre><code>vi ~/.zshrc</code></pre><ol start="2">
<li>환경 변수 설정을 위해 아래의 코드를 작성 후 저장한다.</li>
</ol>
<pre><code>// M1 칩인 경우
export PATH=/opt/homebrew/bin:$PATH

// M1 칩이 아닌 경우
export PATH=/usr/local/bin:/usr/local/sbin:$PATH</code></pre><ol start="3">
<li>이후 코드 적용을 해주면 된다.<pre><code>source ~/.zshrc</code></pre><h2 id="2nodejs-설치">2.Node.js 설치</h2>
<hr/>

</li>
</ol>
<h3 id="1-nodejs-설치">1. Node.js 설치</h3>
<p>React Native는 Javascript의 언어로 된 프레임워크라서 Javascript의 런타임인 Node.js가 필요하다.</p>
<p>brew로 깔아주면 된다.</p>
<pre><code>brew install node</code></pre><h3 id="2-nodejs-버전-확인">2. Node.js 버전 확인</h3>
<p>잘 깔리면 버전이 나온다.</p>
<pre><code>npm --version</code></pre><p><img src="https://velog.velcdn.com/images/developer-sora/post/8c4a5cee-07e1-4a1f-b607-809dc1c9d59b/image.png" alt=""></p>
<h2 id="3watchman-설치">3.Watchman 설치</h2>
<hr/>

<p>React Native에서는 디버그 모드에서 소스코드의 추가, 변경이 발생하면 hot-reload를 하기 위해 Watchman을 사용하고 있다.</p>
<p>Watchman은 특정 폴더나 파일을 감시하다가 변화가 생기면, 특정 동작을 실행하도록 설정하는 역할을 한다.</p>
<h3 id="1-watchman-설치">1. Watchman 설치</h3>
<p>brew로 Watchman을 설치해준다.</p>
<pre><code>brew install watchman</code></pre><h3 id="2-watchman-버전-확인">2. Watchman 버전 확인</h3>
<p>Watchman이 잘 설치되었다면, 아래 명령어를 통해 버전을 확인할 수 있다.</p>
<pre><code>watchman --version</code></pre><p><img src="https://velog.velcdn.com/images/developer-sora/post/1551c1c7-0693-4af4-9baa-77dd1c71e8d9/image.png" alt=""></p>
<h2 id="4xcode-설치">4.Xcode 설치</h2>
<hr/>
React Native로 iOS 앱을 개발하기 위해서는 iOS 개발 툴인 Xcode가 필요하다.

<p>Xcode는 iOS를 위한 앱을 만들 수 있는 공식 통합 개발 환경(IDE)이다.</p>
<p>설치하는데 오래 걸리니 미리미리 깔아두자.</p>
<h3 id="1-xcode-설치">1. Xcode 설치</h3>
<p>아래 링크를 통해 Xcode를 다운로드한다.</p>
<p><a href="https://apps.apple.com/us/app/xcode/id497799835?mt=12">https://apps.apple.com/us/app/xcode/id497799835?mt=12</a></p>
<h3 id="2-command-line-tools-설정">2. Command Line Tools 설정</h3>
<p>Xcode &gt; Settings &gt; Locations에서 가장 최신의 Command Line Tools를 선택해준다.
<img src="https://velog.velcdn.com/images/developer-sora/post/b3ae40f7-5b83-469d-a398-82d410b017a0/image.png" alt=""></p>
<h3 id="3-cocoapods-설치">3. Cocoapods 설치</h3>
<p>Cocoapods는 React Native로 iOS 앱을 개발하려면 꼭 필요한 의존성 관리자이다.</p>
<h4 id="1-cocoapaods-설치">1. Cocoapaods 설치</h4>
<p>터미널에 아래 명령어를 입력해서 Cocoapods를 설치한다.</p>
<pre><code>sudo gem install cocoapods</code></pre><h4 id="2-cocoapods-버전-확인">2. Cocoapods 버전 확인</h4>
<p>Cocoapods가 잘 설치되었다면, 아래의 명령어를 통해 Cocoapods의 버전을 확인할 수 있다.</p>
<pre><code>pod --version</code></pre><p><img src="https://velog.velcdn.com/images/developer-sora/post/48650479-a648-419d-895b-8ad834394a48/image.png" alt=""></p>
<h2 id="5jdk-설치">5.JDK 설치</h2>
<hr/>
React Native로 Android 앱을 개발하기 위해서는 JDK(Java Development Kit)가 필요하다.

<p>jdk는 자바 애플리케이션을 구축하기 위한 핵심 플랫폼 구성요소이다.</p>
<p>처음에 jdk8로 받았는데 리액트 네이티브 공식 홈페이지에서 jdk11을 권장한다길래 그걸로 바꿨다.</p>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/881b53c9-0512-4d54-b0d3-71e90a9b86fb/image.png" alt=""></p>
<h3 id="1-jdk-설치">1. JDK 설치</h3>
<p>brew로 JDK를 설치한다.</p>
<pre><code>brew tap AdoptOpenJDK/openjdk
brew install --cask adoptopenjdk11</code></pre><h3 id="2-java-버전-확인">2. Java 버전 확인</h3>
<p>JDK를 통해 Java가 잘 설치됐으면 아래의 명령어를 통해 Java의 버전을 확인할 수 있다.</p>
<pre><code>java -version</code></pre><p><img src="https://velog.velcdn.com/images/developer-sora/post/1f8a3d85-1234-4c9b-9aef-58a1d3636e3a/image.png" alt=""></p>
<h2 id="6android-studio-설치">6.Android Studio 설치</h2>
<hr/>
React Native로 Android 앱을 개발하기 위해서는 Android 개발 툴인 Android Studio가 필요하다.

<p>Android Studio는 Android를 위한 앱을 만들 수 있는 공식 통합 개발 환경(IDE)이다.</p>
<h3 id="1-android-studio-설치">1. Android Studio 설치</h3>
<p>아래의 링크를 통해 Android Studio를 다운로드 한다.
<a href="https://developer.android.com/studio">https://developer.android.com/studio</a></p>
<h3 id="2-sdk-설정">2. SDK 설정</h3>
<p>Android Studio 설치가 완료되면, SDK를 설정해야 한다.</p>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/533a2236-7a72-4f8d-a706-3f142729fde3/image.png" alt=""></p>
<p>자신에게 필요한 API Level에 맞는 SDK Platform을 선택하여, 아래의 내용을 찾아 OK 버튼을 누르면 된다.</p>
<p>나는 31버전을 골랐다. </p>
<blockquote>
</blockquote>
<ul>
<li>Android SDK Platform 30</li>
<li>Intel x86 Atom System Image</li>
<li>Google APIs Intel x86 Atom System Image</li>
<li>Google APIs Intel x86 Atom_64 System Image</li>
</ul>
<p>이것들을 다운받으면 된다.</p>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/0c7005c0-b678-436a-ac4e-b29a97761d20/image.png" alt=""></p>
<h3 id="3-vdm-설정">3. VDM 설정</h3>
<p>Android Studio 설치가 완료되면, VDM(Virtual Device Manager)을 설정해야 한다.</p>
<p>Android Studio의 More Actions &gt; Virtual Device Manager탭에서 Create device를 클릭한다.</p>
<p>Google Play Store의 표시가 있는 device를 선택하고(가장 최신의 device를 선택하면 됨), 아까 선택한 API Level에 맞는 System Image를 선택하면 된다.</p>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/2eba04c6-799f-4674-acbe-41111c38d249/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/adb99a99-208b-4503-ad07-e8965de3dcc9/image.png" alt=""></p>
<p>선택한 AVD가 뜨고 Finish를 누르면 device가 생성된 걸 확인할 수 있다.</p>
<h3 id="4-android-환경-변수-설정">4. Android 환경 변수 설정</h3>
<p>Android Studio를 환경 변수에 등록해 주어야 한다.</p>
<p>Android SDK 메뉴 상단에 있는 Android SDK Location의 위치를 복사한다.
<img src="https://velog.velcdn.com/images/developer-sora/post/e504f55d-3d2b-4f37-94c5-ea6a9bdfb4c9/image.png" alt=""></p>
<p>터미널에 아래 명령어를 입력하여 파일을 열어서 수정하면 된다.</p>
<pre><code>open ~/.zshrc</code></pre><blockquote>
</blockquote>
<p>export ANDROID_HOME=복사한 자신의 안드로이드 SDK 위치
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/tools
export PATH=$PATH:$ANDROID_HOME/tools/bin
export PATH=$PATH:$ANDROID_HOME/platform-tools</p>
<blockquote>
</blockquote>
<p>첨에 $ 안넣었다가 zshrc 오류나서 다 날라가는 줄 알았다 😭</p>
<h3 id="5-adb">5. adb</h3>
<p>안드로이드 SDK가 잘 설정되었다면, 아래의 명령어를 통해 Android의 버전을 확인할 수 있다.</p>
<h2 id="7react-native-프로젝트-생성-및-확인">7.React Native 프로젝트 생성 및 확인</h2>
<p>대망의 마지막 프로젝트 생성 단계..!
사실 한번에 성공할거라고는 생각 안하는게 좋다. 😅</p>
<h3 id="1-react-native-프로젝트-생성">1. React Native 프로젝트 생성</h3>
<p>처음에 글로벌로 CLI를 깔았었는데 <code>TypeError: cli.init is not a function</code> 에러가 나길래 전역으로 설치된 버전을 지우고 그냥 npx로 실행시켰다.</p>
<p>React Native에 내장된 명령줄 인터페이스를 사용하여 새 프로젝트를 생성한다.</p>
<pre><code>npx react-native@latest init MyApp</code></pre><p>지우는건 이걸로 지우면 된다.</p>
<pre><code>npm uninstall -g react-native-cli @react-native-community/cli react-native
npm -g list</code></pre><h3 id="2-ios에서-확인하기">2. iOS에서 확인하기</h3>
<p>아래 명령어로 프로젝트를 실행하여 simulator에서 잘 작동하는지 확인해보자.</p>
<pre><code>npm run ios</code></pre><p><img src="https://velog.velcdn.com/images/developer-sora/post/72b3fe96-4d8c-4caf-9d67-3e8bed54d711/image.png" alt=""></p>
<p>야호~~</p>
<h3 id="3-android에서-확인하기">3. Android에서 확인하기</h3>
<p>아래 명령어로 프로젝트를 실행하여 emulator에서 잘 작동하는지 확인해보자.</p>
<pre><code>npm run android</code></pre><p><img src="https://velog.velcdn.com/images/developer-sora/post/c693b2c4-5c65-4145-ac57-89052e140af8/image.png" alt=""></p>
<p>고생하셨습니다~~ 👏👏👏👏👏 
<img src="https://velog.velcdn.com/images/developer-sora/post/90f7132c-8bba-43e0-b1f2-a025445a84d2/image.png" alt=""></p>
<h2 id="그-외">그 외</h2>
<p>Ruby 버전이 맞지 않는 경우 </p>
<ol>
<li><p>rbenv 설치하기</p>
<pre><code>brew install rbenv</code></pre></li>
<li><p>ruby 설치하기</p>
<pre><code>rbenv install 3.2.2</code></pre><ol start="3">
<li>ruby 기본 설정하기</li>
</ol>
<p>기본적으로 <code>system</code> 버전으로 되어있기 때문에 방금 install한 버전으로 변경해줘야함.</p>
<pre><code>rbenv versions
=&gt; *system
=&gt; 3.2.2</code></pre></li>
</ol>
<ol start="4">
<li><code>global</code>을 사용해 변경 후, <code>rbenv versions</code> 로 확인해 보면 변경되어 있다.<pre><code>rbenv global 3.2.2
</code></pre></li>
</ol>
<p>rbenv versions
=&gt; system
=&gt; * 3.2.2</p>
<pre><code> 5. ruby 버전 확인하기</code></pre><p>ruby --version
=&gt; ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22]</p>
<pre><code> 6. 버전이 잘 변경되었다면 ruby 패키지 관리자 bundler 설치</code></pre><p>gem install bundler
 ```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] new Array().fill()로 값 할당할 때 주의점]]></title>
            <link>https://velog.io/@developer-sora/TIL-new-Array.fill%EB%A1%9C-%EA%B0%92-%ED%95%A0%EB%8B%B9%ED%95%A0-%EB%95%8C-%EC%A3%BC%EC%9D%98%EC%A0%90</link>
            <guid>https://velog.io/@developer-sora/TIL-new-Array.fill%EB%A1%9C-%EA%B0%92-%ED%95%A0%EB%8B%B9%ED%95%A0-%EB%95%8C-%EC%A3%BC%EC%9D%98%EC%A0%90</guid>
            <pubDate>Fri, 20 Oct 2023 08:15:13 GMT</pubDate>
            <description><![CDATA[<p>백준 1149번 RGB거리 문제를 푸는데
점화식은 맞았는데 자꾸 값이 이상하게 나오는 것이다.🤯</p>
<p>그래서 for문에 console을 찍어봤는데 나는 분명 첫번째 배열 요소에 값을 넣었는데 2번째 배열 요소에도 똑같은 값이 들어간 것이였다!</p>
<pre><code class="language-javascript">let dp = new Array(N).fill([Infinity, Infinity, Infinity]);

dp[0] = cost[0];

for (let i = 1; i &lt; N; i++) {
  dp[i][0] = Math.min(dp[i - 1][1] + cost[i][0], dp[i - 1][2] + cost[i][0]);
  dp[i][1] = Math.min(dp[i - 1][0] + cost[i][1], dp[i - 1][2] + cost[i][1]);
  dp[i][2] = Math.min(dp[i - 1][0] + cost[i][2], dp[i - 1][1] + cost[i][2]);
}</code></pre>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/0035a229-d338-484a-9b9c-0beb151505aa/image.png" alt=""></p>
<p>전부 Infinity로 초기화했지만, 첫번째 요소로 for문이 순회하는 중인데도 나머지 다른 배열이 채워져있는 괴현상이 일어난 것이다.🫠</p>
<p>내가 기대한 값은
<img src="https://velog.velcdn.com/images/developer-sora/post/c62edac2-670d-487d-9b4e-6eaad689aca5/image.png" alt=""></p>
<p>첫번째 요소만 채우고 나머지가 Infinity로 남아있는 것이였다.</p>
<p>그래서 구글링을 해보니까 fill 메서드가 얕은 복사로 값을 채우기 때문에 배열이 들어가게 되면 모든 배열이 같은 참조값을 바라본다는 것이였다.</p>
<p>따라서 내가 첫번째 배열에 값을 넣었으면 나머지 배열에도 값이 들어가는 것이다!!!(메모리 주소 값이 같으므로)</p>
<p>그래서 1차원 배열에서 나지 않았던 오류가 2차원 배열에서 나게 된 것이다...</p>
<p>그러면 어떻게 초기화를 하느냐</p>
<pre><code class="language-javascript">let dp = Array.from({ length: N }, () =&gt; [Infinity, Infinity, Infinity]);</code></pre>
<p>이렇게 초기화 하면 전부 다른 참조값을 가진 2차원 배열을 만들 수 있다.</p>
<p><strong>교훈)</strong>
자바스크립트 메서드를 잘 알고 쓰자..</p>
<blockquote>
<p>참조
<a href="https://aeunhi99.tistory.com/257">https://aeunhi99.tistory.com/257</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 반응형 이미지]]></title>
            <link>https://velog.io/@developer-sora/TIL-%EB%B0%98%EC%9D%91%ED%98%95-%EC%9D%B4%EB%AF%B8%EC%A7%80</link>
            <guid>https://velog.io/@developer-sora/TIL-%EB%B0%98%EC%9D%91%ED%98%95-%EC%9D%B4%EB%AF%B8%EC%A7%80</guid>
            <pubDate>Thu, 21 Sep 2023 08:52:45 GMT</pubDate>
            <description><![CDATA[<h1 id="반응형-이미지">반응형 이미지</h1>
<h2 id="반응형-이미지란">반응형 이미지란</h2>
<p>화면 크기, 해상도 및 기타 기능이 여러 디바이스에서 잘 작동하는 이미지를 말한다.</p>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/3a75a2b8-e281-469b-8369-14ea0f49821d/image.gif" alt=""></p>
<p>이미지 출처 
<a href="https://tenor.com/view/responsive-web-design-gif-13156874">https://tenor.com/view/responsive-web-design-gif-13156874</a></p>
<h2 id="반응형-이미지가-필요한-이유">반응형 이미지가 필요한 이유</h2>
<p>넓은 화면에서 볼 땐 괜찮았던 이미지가 좁은 화면에서는 이미지가 작아져서 확인하기 힘든 경우가 있다.</p>
<p>이럴 때 이미지의 중요한 부분만 잘라서 제공하고 싶을 때 사용하는 것이 <code>아트 디렉션</code>이다.</p>
<p><code>아트 디렉션</code>은 다양한 레이아웃에 대해 서로 다른 자른 이미지를 제공하려고 하는 것을 말한다.</p>
<p>또한, 모바일 화면인 경우 큰 이미지를 삽입할 필요가 없고,(대역폭 낭비) 작은 이미지의 경우 원래 크기보다 크게 표시되면 깨지는 경우가 있다.</p>
<p>벡터 이미지(svg)가 이런 문제를 어느 정도 해결할 수 있지만, 사진과 같이 세부 묘사가 포함된 경우 벡터 기반 이미지를 만드는 것은 매우 복잡하다. 벡터 이미지는 단순한 그래픽, 패턴, 인터페이스 요소 등에 적합하지 복잡한 이미지를 표현하는데 적절하지 않다.</p>
<p>이럴 때 화면 크기에 따라 동일한 이미지를 다른 사이즈 별로 제공할 수 있는데, 이를 <code>해상도 전환</code>이라고 한다.</p>
<h2 id="반응형-이미지를-어떻게-만드나요">반응형 이미지를 어떻게 만드나요?</h2>
<h3 id="이미지-비율-맞추기">이미지 비율 맞추기</h3>
<p><code>background-size</code>와 <code>object-fit</code>속성을 사용하여 이미지를 조절할 수 있다.</p>
<p><code>background-size</code>는 배경 이미지가 있는 요소에 작동하는 속성이며 <code>&lt;img&gt;</code>태그에서는 작동하지 않는다.</p>
<p>이 때 사용할 수 있는 속성이 <code>object-fit</code>이다.</p>
<p><strong>키워드 종류</strong></p>
<ul>
<li><p><code>contain</code>: 요소의 크기에 맞게 크기가 조정되고, 비율은 고정된 상태를 말한다.
<img src="https://velog.velcdn.com/images/developer-sora/post/00bfdfe6-4c74-4ce1-a3f5-5aea068c6b3c/image.png" alt=""></p>
</li>
<li><p><code>cover</code>: 요소의 크기에 맞게 크기가 조정되며 가득 채울때까지 확대된다.
<img src="https://velog.velcdn.com/images/developer-sora/post/75493910-37c0-4a99-88e9-3271ef144907/image.png" alt=""></p>
</li>
<li><p><code>fill</code>: 기본값, 요소의 크기에 맞게 꽉 채워준다. 이미지의 비율이 안맞는 경우가 생길 수 있다.
<img src="https://velog.velcdn.com/images/developer-sora/post/9fdae669-264b-470b-93b3-23a130fd34d3/image.png" alt=""></p>
</li>
</ul>
<h3 id="해상도-전환-동일한-크기-다른-해상도">해상도 전환: 동일한 크기, 다른 해상도</h3>
<h4 id=""></h4>
<p><code>srcset</code> 속성에 <code>x</code> 디스크립터를 사용하면 동일한 크기의 이미지를 여러 해상도로 지원할 수 있다.</p>
<pre><code class="language-html">&lt;img
  srcset=&quot;elva-fairy-320w.jpg, 
          elva-fairy-480w.jpg 1.5x,
          elva-fairy-640w.jpg 2x&quot;
  src=&quot;elva-fairy-640w.jpg&quot;
  alt=&quot;요정 옷을 입은 엘바&quot; /&gt;</code></pre>
<p>디스플레이 해상도가 각 CSS 픽셀당 디바이스 픽셀이 1개인 경우(저해상도) <code>elva-fairy-320w.jpg</code> 이미지가 로드되고, 디스플레이 해상도가 CSS 픽셀 당 디바이스 픽셀이 2개 이상인 경우(고해상도) <code>elva-fairy-640w.jpg</code>(2x)이미지가 로드된다.</p>
<p><code>x</code>설명자는 디스플레이 픽셀 밀도라는 한 가지에만 적응될 수 있다. </p>
<p>반응형 이미지는 화질 뿐만 아니라 레이아웃에 따라 크기가 달라지는 경우가 있기 때문에 두 가지 사항을 기반으로 반응형 이미지를 구현해야 한다.</p>
<p>따라서 <a href="https://almanac.httparchive.org/en/2019/media#use-of-html-markup">x 설명자는 유용하지만 반응형 이미지로 잘 사용하지 않는다.</a></p>
<p>이것이 다음에 나올 <code>w</code>설명자와 <code>sizes</code>속성이 필요한 이유이다.(<a href="https://almanac.httparchive.org/en/2019/media#use-of-html-markup">이는 웹에서 반응형 이미지 사용량의 약 85%를 차지한다.</a>)</p>
<h3 id="해상도-전환-다양한-크기">해상도 전환: 다양한 크기</h3>
<p>해상도 전환으로 장치에 따라 동일한 이미지를 더 크거나 더 작게 표시할 수 있다.</p>
<p><code>&lt;img&gt;</code>태그에 <code>srcset</code>및 <code>sizes</code> 속성을 사용하여 브라우저에 여러 추가 이미지를 제공할 수 있다.</p>
<pre><code class="language-html">&lt;img
  srcset=&quot;elva-fairy-480w.jpg 480w,
          elva-fairy-800w.jpg 800w&quot;
  sizes=&quot;(max-width: 600px) 480px,
         800px&quot;
  src=&quot;elva-fairy-800w.jpg&quot;
  alt=&quot;요정 옷을 입은 엘바&quot; /&gt;</code></pre>
<p><code>srcset</code>은 브라우저가 선택할 수 있는 이미지 세트와 각 이미지의 크기를 정의한다.</p>
<ol>
<li><strong>이미지 파일 이름</strong> (<code>elva-fairy-480w.jpg</code>)</li>
<li>이미지의 <strong>고유 픽셀 너비</strong>(<code>480vw</code>)
이미지의 고유 크기는 컴퓨터에서 이미지 파일을 검사하여 확인할 수 있는 실제 크기를 말한다.</li>
</ol>
<p><code>sizes</code>는 일련의 미디어 조건(예:화면 너비)을 정의하고 특정 미디어 조건에 해당할 때 어떤 이미지 크기를 선택하는 것이 가장 좋을지 알려준다.</p>
<ol>
<li>미디어 조건(<code>(max-width:600px)</code>)
&quot;뷰포트 너비가 600픽셀 이하일 때&quot;</li>
<li>미디어 조건이 참일 때 이미지가 채울 슬롯의 너비(<code>480px</code>)</li>
</ol>
<p>이러한 속성을 설정하면 브라우저는 다음과 같이 작동한다.</p>
<ol>
<li>기기 너비를 확인</li>
<li><code>sizes</code>목록에서 어떤 미디어 조건이 가장 먼저 참인지 확인</li>
<li>해당 미디어 쿼리에 지정된 슬롯 크기를 확인</li>
<li>슬롯과 크기가 같은 <code>srcset</code>목록에 참조된 이미지 또는 이미지가 없는 경우 선택한 슬롯 크기보다 큰 첫 번째 이미지를 로드</li>
</ol>
<p>즉, 뷰포트 너비가 <code>480px</code>인 브라우저가 페이지를 로드하면 <code>(max-width:600px)</code>이 참이 되므로 브라우저는 <code>480px</code>슬롯을 선택한다.
<code>elva-fairy-480w.jpg</code>가 선택된 슬롯 크기에 가장 가깝기 때문에 로드된다.</p>
<p>800px 사진은 디스크에서 128KB인 반면 480px 버전은 63KB에 불과하므로 65KB나 절약할 수 있다!</p>
<h3 id="아트-디렉션">아트 디렉션</h3>
<p>아트 디렉션 문제는 다양한 디스플레이 크기에 맞게 표시되는 이미지를 변경하는 경우(이미지를 자르는 경우)를 말한다.</p>
<p><code>&lt;picture&gt;</code>요소를 사용하여 구현할 수 있다.</p>
<pre><code class="language-html">&lt;picture&gt;
  &lt;source media=&quot;(max-width: 799px)&quot;
          srcset=&quot;elva-480w-close-portrait.jpg&quot; /&gt;
  &lt;source media=&quot;(min-width: 800px)&quot;
          srcset=&quot;elva-800w.jpg&quot; /&gt;
  &lt;img src=&quot;elva-800w.jpg&quot; 
       alt=&quot;딸 엘바를 안고 서 있는 크리스&quot; /&gt;
&lt;/picture&gt;</code></pre>
<h4 id="아트-디렉션은-다른-유용한-기능을-제공한다">아트 디렉션은 다른 유용한 기능을 제공한다.</h4>
<ul>
<li><a href="https://webkit.org/blog/8840/dark-mode-support-in-webkit/#:~:text=Images%20and%20Dark%20Mode">다크 모드 사용자를 위한  Dark-ify™ 이미지</a></li>
<li><a href="https://bradfrost.com/blog/post/reducing-motion-with-the-picture-element/">&#39;감소된 동작 선호&#39; 접근성 기본 설정을 사용하는 사용자에게 애니메이션 Gif 보내지 않기</a></li>
<li><a href="https://www.w3.org/TR/respimg-usecases/videos/screenrmx1.m4v">이미지 재정렬</a></li>
<li><a href="https://blog.twitter.com/engineering/en_us/topics/infrastructure/2019/capping-image-fidelity-on-ultra-high-resolution-devices">최대 해상도 제한을 설정하여 3x 이상의 장치에서 많은 바이트를 절약</a></li>
<li><a href="https://codepen.io/tigt/post/when-responsive-images-get-ugly#printer-friendly-9">정적, 고해상도, 흑백 이미지를 프린터 및 e-잉크장치로 보냄</a></li>
</ul>
<h4 id="최신-이미지-형식에-대한-대체">최신 이미지 형식에 대한 대체</h4>
<p><code>&lt;picture&gt;</code>요소는 최첨단 형식의 이미지를 지원하는 브라우저에는 해당 형식의 이미지를 로드하고, 이를 지원하지 않는 브라우저에는 대체 이미지를 로드한다. </p>
<pre><code class="language-html">&lt;picture&gt;
  &lt;source srcset=&quot;party.webp&quot;&gt;
  &lt;img src=&quot;party.jpg&quot; alt=&quot;A huge party with cakes.&quot;&gt;
&lt;/picture&gt;</code></pre>
<p>이는 WebP 이미지를 지원하는 브라우저에 WebP 이미지를 제공하고 다른 브라우저에는 JPEG 이미지로 대체된다.</p>
<h2 id="자동화된-반응형-이미지">자동화된 반응형 이미지</h2>
<p>반응형 이미지를 지원하는 소프트웨어</p>
<ul>
<li><a href="https://www.responsivebreakpoints.com/">Cloudinary의 반응형 breakpoint 도구</a></li>
<li><a href="https://make.wordpress.org/core/2015/11/10/responsive-images-in-wordpress-4-4/">WordPress</a></li>
<li><a href="https://www.gatsbyjs.com/plugins/gatsby-plugin-image">Gatsby의 gatsby-plugin-image</a></li>
</ul>
<h2 id="css나-자바스크립트를-사용하면-안되는-이유">CSS나 자바스크립트를 사용하면 안되는 이유?</h2>
<p>브라우저가 페이지를 로드할 때 CSS와 JavaScript를 로드하고 해석하기 전에 이미지를 다운로드 한다. <code>&lt;img&gt;</code>요소를 로드한 다음 Javascript로 뷰포트 너비를 감지한 다음 원하는 경우 이미지를 동적으로 변경할 수 없다. 그러면 원본 이미지와 작은 이미지가 함께 로드되므로 반응형 이미지 측면에서 좋지 않다.</p>
<h2 id="요약">요약</h2>
<p><strong>성능 향상</strong>이 목표라면 <code>&lt;img&gt;</code>,<code>srcset</code>을 사용하고, <strong>디자인 제어</strong>가 필요하다면 <code>&lt;picture&gt;</code>을 사용하자.</p>
<p>전자의 경우 큰 화면에는 큰 이미지를 보여주고, 작은 화면에서는 작은 이미지를 보여줌으로써 바이트를 절약하기 때문에 <strong>비용 절감 효과</strong>가 있다.</p>
<p>후자의 경우 화면 크기와 레이아웃의 차이에 따라 이미지를 다르게 잘라서 제공함으로써 <strong>다양한 이미지</strong>를 제공할 수 있다.</p>
<blockquote>
<p>참조
<a href="https://developer.mozilla.org/ko/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images">https://developer.mozilla.org/ko/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images</a>
<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit">https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit</a>
<a href="https://css-tricks.com/a-guide-to-the-responsive-images-syntax-in-html/#aa-increased-performance">https://css-tricks.com/a-guide-to-the-responsive-images-syntax-in-html/#aa-increased-performance</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[오딘 프로젝트] 반응형 디자인]]></title>
            <link>https://velog.io/@developer-sora/%EC%98%A4%EB%94%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%B0%98%EC%9D%91%ED%98%95-%EB%94%94%EC%9E%90%EC%9D%B8</link>
            <guid>https://velog.io/@developer-sora/%EC%98%A4%EB%94%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%B0%98%EC%9D%91%ED%98%95-%EB%94%94%EC%9E%90%EC%9D%B8</guid>
            <pubDate>Thu, 21 Sep 2023 06:38:31 GMT</pubDate>
            <description><![CDATA[<h2 id="반응형-디자인-소개">반응형 디자인 소개</h2>
<h3 id="반응형-디자인이란-무엇입니까">반응형 디자인이란 무엇입니까?</h3>
<blockquote>
<p>모든 디바이스에서 작동하는 무언가를 만들기 위해 브라우저의 크기의 변화에 반응하는 웹 사이트를 만들 때 사용되는 용어입니다. </p>
</blockquote>
<h2 id="자연스러운-반응성">자연스러운 반응성</h2>
<h3 id="뷰포트-메타-태그">뷰포트 메타 태그</h3>
<blockquote>
<p><code>&lt;meta&gt;</code>태그를 사용하면 모바일 기기에서 실제 렌더링되는 영역과 뷰포트의 크기를 조절할 수 있다.</p>
</blockquote>
<pre><code>&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;</code></pre><blockquote>
</blockquote>
<h3 id="고정-너비를-피해야-하는-이유는-무엇입니까">고정 너비를 피해야 하는 이유는 무엇입니까?</h3>
<blockquote>
<p>고정 너비를 정하면 그 너비 이하로 줄어들 수 없기 때문에
<code>width</code>를 <code>max-width</code>나 <code>min-width</code>로 바꾸는 것이 좋다.</p>
</blockquote>
<h3 id="고정-높이를-피해야-하는-이유는-무엇입니까">고정 높이를 피해야 하는 이유는 무엇입니까?</h3>
<blockquote>
<p>화면이 작아질 때 해당 <code>div</code> 안의 콘텐츠가 넘칠 수 있다.
<code>height</code>를 <code>min-height</code>로 변경하면 해결된다.
<code>margin</code>과 <code>padding</code>을 사용하면 내부 콘텐츠의 내용에 관계없이 요소를 유연하게 유지할 수 있다.</p>
</blockquote>
<h3 id="어떤-상황에서-고정된-높이나-너비를-사용하는게-적절할까요">어떤 상황에서 고정된 높이나 너비를 사용하는게 적절할까요?</h3>
<blockquote>
<p>일반적으로 너비가 작을수록 고정하는 것이 좋다.
ex) <code>32px</code>사이즈의 아이콘, 사이드바, 헤더, 푸터</p>
</blockquote>
<h3 id="왜-css에서-percentages를-피해야-합니까">왜 CSS에서 percentages를 피해야 합니까?</h3>
<blockquote>
<p>percentage를 사용하지 않아도 대부분의 요소는 이미 뷰포트에 반응한다. 
요소의 너비를 정의하기 위해 percentage 대신 정적 여백(margin)을 사용하자!</p>
</blockquote>
<h4 id="percentage-사용">percentage 사용</h4>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/cee795de-be38-4a7b-a3b6-81c1786607ee/image.gif" alt=""></p>
<p>화면 크기가 변경됨에 따라 카드 양쪽의 여백이 변경된다.</p>
<h4 id="너비-규칙-제거">너비 규칙 제거</h4>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/1447bbed-9a50-4fa5-a7e9-8cd442078526/image.gif" alt=""></p>
<h2 id="반응형-이미지">반응형 이미지</h2>
<h3 id="object-fit과-background-size의-주요-차이점은-무엇인가요"><code>object-fit</code>과 <code>background-size</code>의 주요 차이점은 무엇인가요?</h3>
<blockquote>
<p><code>object-fit</code>은 <code>img</code>나 <code>video</code>태그에 사용되고, <code>background-size</code>는 배경 이미지가 있는 요소에 작동하는 속성이고, <code>img</code> 태그에서는 동작하지 않습니다.</p>
</blockquote>
<h3 id="img를-왜곡하지-않고-어떻게-너비와-높이를-정의할-수-있나요">img를 왜곡하지 않고 어떻게 너비와 높이를 정의할 수 있나요?</h3>
<blockquote>
<p><code>object-fit</code>, <code>background-size</code> 사용</p>
</blockquote>
<h3 id="다양한-화면-해상도에서-다양한-이미지를-제공하려는-이유는-무엇인가요">다양한 화면 해상도에서 다양한 이미지를 제공하려는 이유는 무엇인가요?</h3>
<blockquote>
<ul>
<li>모바일 화면의 경우 큰 이미지를 삽입할 필요가 없다.(대역폭이 낭비된다)</li>
</ul>
</blockquote>
<h3 id="언제-img와-srcset-vs-picture을-사용하고-싶나요">언제 <code>img</code>와 <code>srcset</code> vs <code>picture</code>을 사용하고 싶나요?</h3>
<blockquote>
<p>성능 향상이 필요할땐 <code>img</code>, 디자인 컨트롤이 필요할 땐 <code>picture</code>을 사용한다.</p>
</blockquote>
<blockquote>
<p>참조
<a href="https://codyloyd.com/2021/percentages/">https://codyloyd.com/2021/percentages/</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[프론트엔드 면접 질문 정리 Part2(Frontend,HTML/CSS)]]></title>
            <link>https://velog.io/@developer-sora/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EB%A9%B4%EC%A0%91-%EC%A7%88%EB%AC%B8-%EC%A0%95%EB%A6%AC-Part2HTMLCSS-Frontend</link>
            <guid>https://velog.io/@developer-sora/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EB%A9%B4%EC%A0%91-%EC%A7%88%EB%AC%B8-%EC%A0%95%EB%A6%AC-Part2HTMLCSS-Frontend</guid>
            <pubDate>Wed, 13 Sep 2023 09:07:20 GMT</pubDate>
            <description><![CDATA[<h2 id="frontend">Frontend</h2>
<h3 id="브라우저-렌더링-과정을-설명해주세요">브라우저 렌더링 과정을 설명해주세요.</h3>
<p>클라이언트가 서버에 파일을 요청하면 HTML 데이터를 받게 되는데, 여기서 HTML 마크업을 파싱해서 <code>DOM Tree</code>를 생성하고 CSS 마크업으로 <code>CSSOM Tree</code>를 생성합니다.
<code>DOM Tree</code>와 <code>CSSOM Tree</code>를 바탕으로 <code>Render Tree</code>를 생성하고 이를 기반으로 레이아웃을 계산 합니다. 이 레이아웃을 바탕으로 실제 화면에 나타냅니다.</p>
<h3 id="webpack-babel-polyfill에-대해-설명해주세요">Webpack, Babel, Polyfill에 대해 설명해주세요.</h3>
<ul>
<li><p><strong>Webpack</strong>
웹팩은 자바스크립트 모듈 번들러입니다. 여러 개의 파일을 하나로 번들링하는(묶는) 이유는 브라우저가 페이지를 요청할 때 파일을 여러 번 요청하는 것 보다 한 번 요청하는 것이 웹페이지를 사용자에게 빠르게 전달할 수 있기 때문입니다.</p>
</li>
<li><p><strong>Babel</strong>
바벨은 자바스크립트 컴파일러입니다. 바벨에는 폴리필뿐만 아니라 최신 JavaScript문법 제공, JSX 문법 변환, TypeAnnotation 기능 등을 제공합니다.</p>
</li>
<li><p><strong>Polyfill</strong>
폴리필은 최신 ECMAScript 환경을 만들어줍니다. 최신 기술을 다양한 브라우저에서 사용 가능하게 합니다.</p>
</li>
</ul>
<h3 id="csr과-ssr의-차이는-무엇인가요">CSR과 SSR의 차이는 무엇인가요?</h3>
<p><code>CSR</code>은 클라이언트 사이드 렌더링이라는 뜻으로 초기 로드 시 빈 <code>HTML</code>과 <code>Javascript</code>를 로드하는데 빈 <code>HTML</code>에 <code>Javascript</code>를 이용해서 <code>DOM</code>을 동적으로 생성해서 그리게 됩니다.
동적으로 <code>DOM</code>을 그리기 때문에 원하는 내용만 업데이트 할 수 있다는 장점이 있습니다.
하지만 첫 로드 시 모든 로직이 담겨있는 <code>Javascript</code>를 다운로드 하다 보니 로딩 속도가 길다는 단점이 있습니다.</p>
<p><code>SSR</code>은 서버 사이드 렌더링이라는 뜻으로 서버에서 렌더링하여 완성된 <code>HTML</code> 파일을 로드해줍니다. <code>SSR</code>은 페이지의 <code>HTML</code>을 다운받기 때문에 <code>CSR</code>보다 초기 로딩이 빠르고, 개별 페이지를 넘겨 받기 때문에 각 페이지에 대한 정보를 입력할 수 있어 SEO를 향상시킬 수 있습니다.
하지만 링크 이동시 화면 깜빡임 현상이 있을 수 있고, 페이지 이동이 <code>CSR</code>보다 느립니다. 그리고 <code>Javascript</code> 다운이 늦어진다면 기능이 동작하지 않는 경우가 있습니다.</p>
<h3 id="cors는-무엇인지-이를-처리를-해본-경험을-말씀해주세요">CORS는 무엇인지, 이를 처리를 해본 경험을 말씀해주세요.</h3>
<p><code>CORS</code>란 교차 출처 리소스 공유로 한 출처에서 다른 출처의 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제를 말합니다. 
한 출처에서 다른 출처의 자원의 접근을 막는 이유는 보안 때문인데, 예전에는 이것을 절대적으로 지켰지만 기술이 발달하면서 서로 다른 출처끼리 데이터를 주고 받아야 하는 일이 많아졌고, 이에 따라 몇 가지 예외 상황에 대해서는 다른 출처로도 요청을 보낼 수 있게 허락하는 <code>CORS</code>가 나타나게 되었습니다.
<code>CORS</code> 에러를 해결하기 위해서는 서버측에서 접근 권한을 주는 헤더를 추가하거나 리액트에서 프록시 속성을 설정하는 경우가 있습니다.</p>
<h3 id="웹-표준을-지키며-개발하시나요">웹 표준을 지키며 개발하시나요?</h3>
<ol>
<li>DOCTYPE 선언</li>
<li>시맨틱 태그 사용</li>
<li>CSS 스타일 시트 사용</li>
<li>웹 접근성 고려(alt, aria-label...)</li>
</ol>
<h3 id="쿠키와-세션-웹스토리지에-대해-설명해주세요">쿠키와 세션, 웹스토리지에 대해 설명해주세요.</h3>
<p>기본적으로 <code>HTTP</code> 프로토콜 환경은 무상태성이기 때문에 서버는 클라이언트가 누구인지 매번 확인해야 합니다. 이를 보완하기 위해 쿠키와 세션, 웹스토리지를 사용할 수 있습니다.</p>
<p><strong>쿠키</strong>
쿠키는 클라이언트 로컬에 저장되는 데이터로 유효 기간이 지나지 않으면 브라우저가 종료되어도 유지된다는 특징이 있습니다.</p>
<p><strong>세션</strong>
세션은 서버 측에 저장되며 브라우저가 종료될 때까지만 상태를 유지합니다. 데이터가 서버에 저장되기 때문에 쿠키보다 보안이 좋지만 사용자가 많아질수록 서버 메모리를 많이 차지하게 됩니다.</p>
<p><strong>웹 스토리지</strong>
웹 스토리지는 클라이언트에 데이터를 저장할 수 있도록 <code>HTML5</code>부터 추가된 저장소로 로컬 스토리지와 세션 스토리지가 있습니다.
로컬 스토리지는 사용자가 데이터를 지우지 않는 이상 브라우저를 종료해도 계속 남아있고 세션 스토리지는 브라우저가 종료될 경우 데이터가 제거됩니다.</p>
<h3 id="타입스크립트를-사용하는-이유는-무엇인가요">타입스크립트를 사용하는 이유는 무엇인가요?</h3>
<p>타입스크립트는 컴파일 언어이기 때문에 컴파일 과정에서 오류를 잡아내기 때문에 오류를 잡기 쉽습니다.
또한 타입스크립트를 사용하면 어떤 타입의 값을 리턴해야 하고 어떤 타입의 매개변수가 들어가는지 쉽게 알 수 있기 때문에 가독성이 좋고 협업에 도움이 됩니다.
또한 VSCode에서 타입스크립트를 사용하면 자동완성을 지원해주기 때문에 코드를 작성하는데 편리합니다.</p>
<h3 id="크로스-브라우징-경험이-있으신가요">크로스 브라우징 경험이 있으신가요?</h3>
<p><del>예..</del>
리액트 네이티브로 개발을 진행했을 때 안드로이드와 iOS가 사용하는 문법이나 기본으로 적용되어있는 CSS가 달라 두 기기에서 테스트 해보면서 작업을 했던 경험이 있습니다.</p>
<h3 id="웹-소켓을-사용해보셨나요">웹 소켓을 사용해보셨나요?</h3>
<p><del>아니요</del>
웹소켓은 클라이언트와 서버를 연결하고 실시간으로 통신이 가능하도록 하는 통신 프로토콜입니다.
웹소켓은 한 번의 요청으로 <code>handShake</code> 과정을 통해 연결을 유지하므로 양방향 통신과 데이터 이동 가능합니다.</p>
<h3 id="웹사이트-성능-최적화에는-어떤-방법이-있나요">웹사이트 성능 최적화에는 어떤 방법이 있나요?</h3>
<ol>
<li>Webpack을 통해 번들 된 자바스크립트 파일의 크기를 최대한 줄입니다.</li>
<li>CSS를 최적화 합니다.(리플로우, 리페인트를 고려한 스타일 작성)</li>
<li>이미지 최적화 합니다.(이미지 지연로딩 활용, 스프라이트 이미지 사용)
기타 등등..</li>
</ol>
<h3 id="bundle의-사이즈를-줄이려면-어떻게-해야-하나요">bundle의 사이즈를 줄이려면 어떻게 해야 하나요?</h3>
<ol>
<li>소스 코드 최소화하기(<code>Minify</code>:압축하기, <code>Uglify</code>:난독화)</li>
</ol>
<ul>
<li>Webpack은 v4이상의 버전에서 mode가 <code>production</code>일 때 <strong>자동으로 압축과 난독화를 진행합니다.</strong> v5에서는 이 과정에서 사용되는 플러그인으로 <code>terser-webpack-plugin</code>을 내장하고 있습니다. 
모종의 이유로 이 과정을 생략하고 싶다면 webpack 설정에 <code>optimization:{minimize: false}</code>를 추가하면 됩니다.</li>
</ul>
<ol start="2">
<li>CSS 압축하기</li>
</ol>
<ul>
<li><p>CSS의 불필요한 공백을 제거하기 위해 Webpack의 <code>CssMinimizerPlugin</code>을 활용합니다. 추가적으로 CSS를 별개의 파일로 분리하기 위해서 <code>MiniCssExtractPlugin</code>을 활용합니다.</p>
<p><strong>style-loader와 MiniCssExtract의 차이</strong></p>
<p><code>style-loader</code>는 DOM에 바로 CSS를 주입하고, <code>MiniCssExtract</code>는 JS 파일 하나 당 다른 CSS 파일을 생성합니다. </p>
<p><code>style-loader</code>는 CSS 파일을 받아오기 위한 요청의 개수를 줄일 수 있어 페이지의 로딩 속도를 단축할 수 있지만 현재 페이지에서 불필요한 CSS까지도 주입될 수 있다는 단점이 있습니다. </p>
<p>반면에 <code>MiniCSsExtract</code>를 활용하면 별도의 요청을 보내야만 CSS를 불러올 수 있지만 현재 페이지에서만 사용될 CSS를 불러오기 때문에 불러올 데이터의 크기가 줄어듭니다.</p>
<p>번들을 나누지 않고 하나의 파일로 생성하거나, 페이지 간에 공유하는 스타일이 많으면 전자를, 코드 스플리팅을 적용해 번들 파일이 여러개이거나 페이지 별로 공유하는 CSS의 크기가 크지 않으면 후자를 선택하는 것이 좋습니다.</p>
</li>
</ul>
<ol start="3">
<li>코드 스플리팅</li>
</ol>
<ul>
<li><p>코드 스플리팅은 반드시 필요한 코드만 불러오고 요청을 병렬적으로 처리해서 로딩 속도를 개선할 수 있습니다.</p>
<p><strong>3-1. webpack 설정</strong>
자동으로 중복되는 코드를 판단해서 별도의 파일을 분리하도록 설정합니다.</p>
<pre><code>output: {
// 번들에 별도의 이름 =&gt; 생성되는 파일 이름 충돌 발생 방지
filename: &#39;[name].bundle.js&#39;, 
path: path.join(__dirname, &#39;/dist&#39;),
clean: true
},
splitChunks: {
chunks: &#39;all&#39;
}</code></pre><p><strong>3-2.React.lazy를 활용해 페이지 분리하기</strong>
  <code>React.lazy</code>와 <code>React Router</code>를 함께 사용해 각 루트를 동적으로 불러오면 코드가 실행됐을 때 그 모듈을 필요한 시점에 불러올 수 있습니다.</p>
</li>
</ul>
<h2 id="html-css">HTML CSS</h2>
<h3 id="cascading에-관해서-설명해주세요">Cascading에 관해서 설명해주세요.</h3>
<p>Cascading은 위에서 아래로 흐른다는 뜻으로 스타일의 적용 규칙을 말합니다. 캐스캐이딩은 중요도, 명시도, 코드 순서 요소를 통해 우선 순위를 결정합니다.</p>
<h3 id="css-애니메이션과-js-애니메이션의-차이에-대해-설명해주세요">CSS 애니메이션과 JS 애니메이션의 차이에 대해 설명해주세요.</h3>
<p>간단한 애니메이션의 경우 CSS로 처리합니다. <strong>CSS 애니메이션은</strong> 외부 라이브러리를 필요로 하지 않고 반응형 구현이 가능하고 브라우저 렌더링 과정에서 layout이나 paint 단계를 거치지 않기 때문에 성능 개선에 효과적입니다.</p>
<p>CSS로 처리하기에 복잡하고 무거운 애니메이션들을 효율적으로 세밀하기 다루기 위해 <strong>JS 애니메이션</strong>을 사용합니다. 크로스 브라우징 측면에서 JS 애니메이션이 유리합니다.</p>
<h3 id="flexbox를-사용해보셨나요">Flexbox를 사용해보셨나요?</h3>
<p>Flexbox는 요소를 한 방향으로 나열시킵니다. 레이아웃을 자유롭게 배치할 때 주로 사용합니다.</p>
<h3 id="grid-를-사용해보셨나요">Grid 를 사용해보셨나요?</h3>
<p>Grid는 2차원 적으로(두 방향)으로 나열시킬 수 있습니다. 주로 고정된 레이아웃에 사용합니다.</p>
<h3 id="position-속성을-나열해주세요">position 속성을 나열해주세요.</h3>
<p>CSS에서 <code>position</code>속성은 기본 값인 <code>static</code>, 원래 위치를 기준으로 상대적으로 배치해주는 <code>relative</code>, 상위 요소를 기준으로 배치하는 <code>absolute</code>, 뷰포트에 고정으로 배치시키는 <code>fixed</code>, 스크롤을 내리다가 요소를 만나면 고정시키는 <code>sticky</code>가 있습니다.</p>
<blockquote>
<p>참조
<a href="https://velog.io/@hozzijeong/WebPack%EA%B3%BC-Babel-%EA%B7%B8%EB%A6%AC%EA%B3%A0-Polyfill%EC%97%90-%EB%8C%80%ED%95%B4">https://velog.io/@hozzijeong/WebPack%EA%B3%BC-Babel-%EA%B7%B8%EB%A6%AC%EA%B3%A0-Polyfill%EC%97%90-%EB%8C%80%ED%95%B4</a>
<a href="https://story.pxd.co.kr/1662">https://story.pxd.co.kr/1662</a>
<a href="https://interconnection.tistory.com/74">https://interconnection.tistory.com/74</a>
<a href="https://medium.com/@uk960214/%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94-1-%EB%B2%88%EB%93%A4-%ED%81%AC%EA%B8%B0-%EC%A4%84%EC%9D%B4%EA%B8%B0-react-webpack-minify-code-splitting-e2391e7e5f1b">https://medium.com/@uk960214/%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94-1-%EB%B2%88%EB%93%A4-%ED%81%AC%EA%B8%B0-%EC%A4%84%EC%9D%B4%EA%B8%B0-react-webpack-minify-code-splitting-e2391e7e5f1b</a>
<a href="https://runcoding.tistory.com/19">https://runcoding.tistory.com/19</a>
그 외 미처 추가하지 못한 출처들</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[프론트엔드 면접 질문 정리 Part1(CS, JS,React)]]></title>
            <link>https://velog.io/@developer-sora/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EB%A9%B4%EC%A0%91-%EC%A7%88%EB%AC%B8-%EC%A0%95%EB%A6%AC-Part1CS-JSReact</link>
            <guid>https://velog.io/@developer-sora/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EB%A9%B4%EC%A0%91-%EC%A7%88%EB%AC%B8-%EC%A0%95%EB%A6%AC-Part1CS-JSReact</guid>
            <pubDate>Tue, 12 Sep 2023 02:32:04 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/developer-sora/post/fbfe1bd5-eedf-4346-afaa-a213c0dada57/image.jpg" alt=""></p>
<h2 id="cs지식">CS지식</h2>
<h3 id="브라우저-주소창에-wwwgooglecom을-입력하면-어떤-일이-일어나나요">브라우저 주소창에 <a href="http://www.google.com%EC%9D%84">www.google.com을</a> 입력하면 어떤 일이 일어나나요?</h3>
<p>주소창에 <code>www.google.com</code>을 입력하면 웹 브라우저는 우선 캐싱된 <code>DNS</code>기록을 찾아보고 기록에 없을 경우 <code>DNS</code>에게 도메인 주소를 요청하여 <code>IP</code>주소를 응답받습니다. 웹 브라우저는 <code>IP</code> 주소를 이용하여 웹 서버에게 <code>HTTP</code> 요청을 하고, 서버에게서 응답 받은 <code>HTML</code>을 화면에 출력합니다.</p>
<h3 id="get과-post의-차이는-무엇인가요">GET과 POST의 차이는 무엇인가요?</h3>
<p><code>GET</code>은 정보를 요청할 때 사용하는 메서드이고, <code>POST</code>는 서버에 데이터를 보내는데 사용됩니다.</p>
<h3 id="객체-지향-프로그래밍이란-무엇인가요">객체 지향 프로그래밍이란 무엇인가요?</h3>
<p>객체 지향 프로그래밍은 컴퓨터 프로그래밍 패러다임 중의 하나로, 프로그래밍에서 필요한 데이터를 추상화시켜 객체로 만들고 그 객체들 간의 <strong>상호작용</strong>을 통해 로직을 구성하는 프로그래밍 방법입니다.</p>
<h3 id="프로세스와-스레드에-대해-설명해주세요">프로세스와 스레드에 대해 설명해주세요.</h3>
<p>프로세스는 메모리 상에서 실행중인 프로그램을 말하고, 스레드는 이 프로세스 안에서 실행되는 흐름 단위를 말합니다.</p>
<h3 id="dns에-대해-설명해주세요">DNS에 대해 설명해주세요.</h3>
<p><code>DNS</code>는 <code>URL</code>과 <code>IP</code> 주소를 변환해주는 역할을 하는 시스템을 말합니다. <code>IP</code>는 숫자로 되어있어 사람이 이해하기 어렵기 때문에 사람이 알아볼 수 있는 <code>URL</code>로 변환하는 작업이 필요합니다.</p>
<h3 id="rest-api에-대해-설명해주세요">REST API에 대해 설명해주세요.</h3>
<p><code>REST</code>를 기반으로 만들어진 <code>API</code>를 말합니다.
<code>REST</code>는 <code>HTTP</code>를 기반으로 필요한 자원에 접근하는 방식을 정해놓은 아키텍처입니다.
<code>REST API</code>는 자원(URI), 행위(HTTP 메서드), 표현(페이로드)으로 이루어져 있으며 <code>HTTP</code> 메서드를 사용해야 하고, <code>URI</code>가 리소스를 표현하는데 집중해야 합니다.</p>
<h2 id="javascript">Javascript</h2>
<h3 id="promise와-callback의-차이를-설명해주세요">Promise와 Callback의 차이를 설명해주세요.</h3>
<p><code>Callback</code>은 비동기 로직의 결과값을 <code>Callback</code> 안에서만 처리를 할 수 있고, 콜백 밖에서 비동기로 온 값을 알 수 없습니다. 하지만 <code>Promise</code>는 비동기에서 온 값이 <code>promise</code> 객체에 저장되어서 코드 작성이 용이합니다. </p>
<h3 id="async-await의-사용-방법을-설명해주세요">async, await의 사용 방법을 설명해주세요.</h3>
<p>함수 앞에 <code>async</code>키워드를 붙이면 이 함수는 항상 <code>promise</code>를 반환합니다. 
<code>await</code>키워드는 <code>async</code>함수 안에서만 동작하는데, <code>await</code>키워드를 <code>promise</code>객체 앞에 붙이면 자바스크립트가 <code>promise</code>를 처리할 때까지 기다리고, 처리가 완료되면 에러가 발생하거나 <code>promise</code>객체의 result 값을 반환합니다.</p>
<h3 id="var-let-const의-차이를-설명해주세요">var, let, const의 차이를 설명해주세요.</h3>
<p><code>var</code>는 중복 선언이 가능하고 선언과 동시에 초기화가 이루어지기 때문에 호이스팅이 발생하는 것 처럼 보여서 코드를 파악하기 힘듭니다.
<code>let</code>과 <code>const</code>는 선언만 이루어지고 초기화는 변수 선언문에서 이루어지기 때문에 호이스팅이 발생하지 않는 것 처럼 보입니다.
<code>let</code>은 중복 선언이 불가능하고, 재할당이 가능합니다.
<code>const</code>는 중복 선언과 재할당이 불가능합니다.</p>
<h3 id="이벤트-버블링과-캡처링에-대해-설명해주세요예시와-함께">이벤트 버블링과 캡처링에 대해 설명해주세요(예시와 함께).</h3>
<p>이벤트 버블링은 한 요소에 이벤트가 발생하면 최상단의 부모 요소의 이벤트까지 전파되는 것을 말합니다.
이벤트 캡쳐는 이벤트 버블링과 반대로 최상위 태그에서 해당 태그로 이벤트가 전파되는 것을 말합니다.</p>
<p><span style='color:#fd254c; background:#FFE9E9'>이벤트 버블링 예시</span>
<code>li</code>태그 각각에 이벤트를 다는 대신 상위<code>ul</code>에 이벤트를 다는 방법</p>
<p><span style='color:#fd254c; background:#FFE9E9'>이벤트 캡처링 예시</span>
버블링보다 성능이 좋다고 하지만.. 구체적인 예시는 조사 필요</p>
<h3 id="클로저에-대해-설명해주세요">클로저에 대해 설명해주세요.</h3>
<p>클로저는 자신이 생성될 때의 환경을 기억하는 함수라고 할 수 있습니다.
클로저는 외부 함수의 실행이 종료되어도 내부 함수에서 외부 함수를 참조하고 있다면 외부 함수 내 변수를 사용할 수 있습니다.</p>
<h3 id="실행-컨텍스트에-대해-설명해주세요">실행 컨텍스트에 대해 설명해주세요.</h3>
<p>실행 컨텍스트는 <strong>실행할 코드에 제공할 환경 정보들을 모아놓은 객체</strong>입니다. 자바스크립트는 <strong>실행 컨텍스트</strong>를 콜스택에 쌓아올린 후 실행해서 코드의 <strong>환경과 순서를 보장</strong>할 수 있습니다.</p>
<h3 id="호이스팅에-대해-설명해주세요">호이스팅에 대해 설명해주세요.</h3>
<p>호이스팅은 스코프 내의 선언을 해당 스코프의 최상단으로 끌어올린 것 처럼 보이는 현상을 말합니다. 자바스크립트 엔진은 런타임 이전에 실행 컨텍스트에 식별자의 정보를 등록하기 때문에 일어나는 현상입니다. </p>
<h3 id="불변성을-유지하려면-어떻게-해야하나요">불변성을 유지하려면 어떻게 해야하나요?</h3>
<p>객체의 불변성을 유지하려면 스프레드 문법을 사용하거나(얕은 복사만 가능), <code>immer</code> 라이브러리를 사용하거나 <code>structuredClone</code> 함수를 사용해 객체의 불변성을 유지할 수 있습니다.</p>
<h3 id="자바스크립트가-유동적인-언어인-이유는-무엇인가요">자바스크립트가 유동적인 언어인 이유는 무엇인가요?</h3>
<p>자바스크립트는 런타임 시점에 타입이 결정되는 동적 언어입니다. 매번 타입을 쓸 필요가 없기 때문에 빠르게 코드를 작성할 수 있습니다.</p>
<h3 id="this에-대해-설명해주세요">this에 대해 설명해주세요.</h3>
<p><code>this</code>는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 <strong>자기 참조 변수</strong>입니다.
<code>this</code>를 통해 자신이 속한 객체나 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있습니다. <code>this</code> 바인딩은 함수 호출 방식에 의해 동적으로 결정됩니다.</p>
<h3 id="콜백-지옥callback-hell을-해결하는-방법을-말씀해주세요">콜백 지옥(Callback hell)을 해결하는 방법을 말씀해주세요.</h3>
<p>콜백 지옥은 비동기 처리를 위해 콜백 함수를 연속해서 사용해서 코드가 깊어지는 것을 말합니다. 이를 해결하기 위해서는 <code>Promise</code>나 <code>Async await</code>을 사용하는 방법이 있습니다.</p>
<h3 id="promise를-사용한-비동기-통신과-async-await를-사용한-비동기-통신의-차이를-설명해주세요">Promise를 사용한 비동기 통신과 async, await를 사용한 비동기 통신의 차이를 설명해주세요.</h3>
<p><code>Promise</code>를 사용하면 <code>.catch()</code>문을 통해 에러 핸들링이 가능하지만 <code>async/await</code>은 에러 핸들링 기능이 없어 <code>try-catch</code>문을 활용해야 합니다.
<code>async/await</code>은 비동기 코드가 동기 코드처럼 읽히게 해줘서 코드 흐름을 이해하기 쉽습니다.</p>
<h3 id="함수-선언형과-함수-표현식의-차이에-대해-설명해주세요">함수 선언형과 함수 표현식의 차이에 대해 설명해주세요.</h3>
<p><strong>함수 선언식</strong>은 함수명이 정의되어 있고, 별도의 할당 명령이 없는 것을 말합니다.
<strong>함수 표현식</strong>은 함수를 별도의 변수에 할당하는 것을 말합니다.</p>
<p>함수 선언식과 함수 표현식의 주요 차이점은 <strong>호이스팅</strong>입니다.
함수 선언식은 함수 전체를 호이스팅해서 함수 선언 전에 함수를 사용할 수 있지만
함수 표현식은 선언부만 호이스팅 되기 때문에 함수 선언 전에 함수를 사용하면 오류가 납니다.</p>
<h3 id="렉시컬-환경lexical-environment에-대해-설명해주세요">렉시컬 환경(Lexical Environment)에 대해 설명해주세요.</h3>
<p>렉시컬 환경은 특정 코드가 선언된 환경을 말합니다.</p>
<h3 id="데이터-타입에-대해-설명해주세요">데이터 타입에 대해 설명해주세요.</h3>
<p>자바스크립트의 데이터 타입은 <strong>원시 타입</strong>과 <strong>객체 타입</strong>으로 나뉩니다. 원시 타입의 값은 <strong>변경 불가능한 값</strong>이며 <strong>값에 의한 전달</strong>이 일어납니다. 객체는 <strong>참조에 의한 전달</strong>이 일어납니다.</p>
<h3 id="깊은-복사와-얕은-복사에-대해-설명해주세요">깊은 복사와 얕은 복사에 대해 설명해주세요.</h3>
<p><span style="background:#ffff9e; fontWeight:bold">얕은 복사</span>는 참조 타입 데이터가 저장한 메모리 주소 값을 복사한 것을 말하고
<span style="background:#ffff9e; fontWeight:bold">깊은 복사</span>는 새로운 메모리 공간을 확보해 완전히 복사하는 것을 말합니다.</p>
<h3 id="requestanimationframe을-사용해본-적-있나요">requestAnimationFrame을 사용해본 적 있나요?</h3>
<p><code>requestAnimationFrame</code> 메서드는 애니메이션 관련 최적화 API 입니다. 자바스크립트로 스타일을 변화시키는 방법은 CSS보다 성능이 좋지 않기 때문에 성능 최적화가 필요합니다.
<code>requestAnimationFrame</code> 함수는 시스템이 프레임을 그릴 준비가 되면 애니메이션 프레임을 호출합니다. 
<code>requestAnimationFrame</code>을 사용하면 페이지가 비활성화 된 경우 페이지 화면 그리기 작업이 일시 중지되기 때문에 CPU 리소스를 낭비하지 않고, 모니터의 주사율을 따라 호출하기 때문에 별도의 반복 플래그가 필요 없습니다.</p>
<h2 id="react">React</h2>
<h3 id="virtual-dom이-무엇이고-작동-원리에-대해-설명해주세요">Virtual DOM이 무엇이고 작동 원리에 대해 설명해주세요</h3>
<p>Virtual DOM은 실제 DOM의 복사본으로, JS 객체 형태로 메모리 안에 저장됩니다. 리액트는 state가 변경될 때 마다 re-rendering이 발생하는데, 이 시점마다 새로운 내용이 담긴 가상돔을 생성하게 됩니다. 렌더링 이전의 가상돔과 업데이트 이후의 가상돔을 비교해 차이가 발생한 부분만을 실제 DOM에 적용하게 됩니다. 리액트는 Batch Update를 통해 변경된 모든 Element들을 집단화 시켜 이를 한번에 실제 DOM에 적용시키기 때문에 효율적입니다.</p>
<p>*어마어마한 DOM조작이 필요할 때 Virtual DOM 사용이 좋음, 그 외는 기존 DOM 조작이 성능이 빠름</p>
<h3 id="react를-사용하는-이유에-대해-말씀해주세요">React를 사용하는 이유에 대해 말씀해주세요.</h3>
<p>가장 큰 이유는 컴포넌트 기반 개발이 가능하다는 점입니다.
컴포넌트 단위 개발은 가독성이 좋고 재사용성, 확장성과 같은 이점이 있습니다.
또한 리액트의 생태계는 여타 다른 라이브러리, 프레임워크 커뮤니티보다 활발하고, React를 기반으로 React Native를 활용해서 모바일 앱을 만들 수 있습니다.</p>
<h3 id="클래스형-컴포넌트와-함수형-컴포넌트의-차이에-대해-설명해주세요">클래스형 컴포넌트와 함수형 컴포넌트의 차이에 대해 설명해주세요.</h3>
<p>클래스형 컴포넌트는 LifeCycle API를 사용하고, 함수형 컴포넌트에서는 Hook을 사용합니다.
그리고 클래스형 컴포넌트는 this.props로 props를 받는데, this가 변경 가능한 object기 때문에 값이 달라질 수 있습니다. 함수 컴포넌트는 인자를 변경할 수 없기 때문에 렌더링 된 값들을 고정시킵니다.</p>
<h3 id="생명-주기-메서드에-대해-설명해주세요">생명 주기 메서드에 대해 설명해주세요.</h3>
<p>리액트 컴포넌트는 생성(mount) &gt; 업데이트(update) &gt; 제거(unmount)의 생명주기를 갖습니다. </p>
<p>📌마운트시 호출되는 메서드</p>
<ul>
<li><code>constructor</code> : 컴포넌트를 새로 만들 때마다 호출되는 클래스 생성자 메서드</li>
<li><code>getDerivedStateFromProps</code> : props 에 있는 값을 state 에 넣을 때 사용하는 메서드</li>
<li><code>render</code> : 준비한 UI를 렌더링하는 메서드</li>
<li><code>componentDidMount</code> : 컴포넌트가 웹 브라우저상에 나타난 후 호출하는 메서드</li>
</ul>
<p>📌업데이트시 호출되는 메서드</p>
<ul>
<li><code>getDerivedStateFromProps</code> : 앞서 Mount 과정에서도 호출되고, props 변화에 따라 state 값에도 변화를 주고 싶을 때 사용</li>
<li><code>shouldComponentUpdate</code> : 컴포넌트가 리렌더링을 해야 할지 말아야 할지를 결정, true 를 반환하면 다음 라이프사이클 메서드를 계속 실행, false 를 반환하면 작업을 중지(리렌더링 X)한다.</li>
<li><code>render</code> : 컴포넌트를 리렌더링한다.</li>
<li><code>getSnapshotBeforeUpdate</code> : 컴포넌트 변화를 DOM에 반영하기 바로 직전에 호출</li>
<li><code>componentDidUpdate</code> : 컴포넌트의 업데이트 작업이 다 끝난 후 호출</li>
</ul>
<p>📌언마운트시 호출되는 메서드</p>
<ul>
<li><code>componentWillUnmount</code> : 컴포넌트가 웹 브라우저상에서 사라지기 전에 호출</li>
</ul>
<h3 id="리액트에서-jsx-문법이-어떻게-사용되나요">리액트에서 JSX 문법이 어떻게 사용되나요?</h3>
<p>JSX는 Javascript에 XML을 추가한 문법으로 하나의 파일에 자바스크립트와 HTML을 동시에 작성할 수 있어서 편리합니다. 리액트 엔진은 JSX코드를 일반 자바스크립트 형태의 코드로 변환합니다.</p>
<blockquote>
<p><strong>JSX 문법이 _반드시 부모 요소 하나가 감싸는 형태_여야 하는 이유?</strong>
Virtual DOM에서 컴포넌트의 변화를 감지할 때 효율적으로 비교할 수 있도록 컴포넌트 내부는 <strong>하나의 DOM 트리 구조로 이루어져야 한다</strong>는 규칙이 있기 때문입니다.</p>
</blockquote>
<h3 id="왜-state를-직접-바꾸지-않고-usestate를-사용해야-하나요">왜 state를 직접 바꾸지 않고 useState를 사용해야 하나요?</h3>
<p>리액트는 메모리 주소를 통해 값이 변경되었다는 것을 판단하기 때문에 새로운 객체를 만들어서 할당해야 합니다. 따라서 이를 위해 useState를 사용해야 합니다.(setState가 실행되면 새 scope를 만들어 state 변수도 새로 만들게 된다.)</p>
<h3 id="usememo와-usecallback에-대해-설명해주세요">useMemo와 useCallback에 대해 설명해주세요.</h3>
<p><code>useMemo</code>는 메모이제이션된 값을 return하는 hook이고, <code>useCallback</code>은 메모이제이션된 함수를 반환하는 hook입니다.</p>
<p><code>useMemo</code>를 사용해야 할 때)
특정한 상황에서 동작되야 하는 함수가 Component의 렌더링 조건에 따라 지속적으로 실행되는 경우</p>
<blockquote>
<p><strong>그럼 useEffect를 사용하면 되지 않나?</strong>
사용 용도가 다릅니다. <code>useEffect</code>는 부수효과를 만들기 위해 사용합니다. 어떤 값이 바뀌었을 때 그 변화를 지정한 함수를 실행함으로써 전파합니다. <code>useMemo</code>는 특정 값을 메모하여 해당 값을 얻는 과정이 불필요하게 반복되는 것을 방지하기 위해 사용합니다.
<code>useEffect</code>는 side-effect를 위한 훅, <code>useMemo</code>는 side-effect가 존재하면 안된다. 즉 순수함수여야 합니다.</p>
</blockquote>
<p><code>useCallback</code>을 사용해야 할 때)
자식 컴포넌트에 함수를 props로 줄 때(자식 컴포넌트의 불필요한 렌더링 방지),
외부에서 값을 가져오는 api를 호출하는 경우</p>
<h3 id="-usememo와-usestate의-차이">+ useMemo와 useState의 차이</h3>
<p><code>useMemo</code>는 의존성에 따라 값이 자동적으로 바뀌는 경우에 사용한다.</p>
<pre><code class="language-javascript">// b와 c를 더한 값이 a가 되는 경우
const a = useMemo(() =&gt; {b + c}, [b, c]);</code></pre>
<p><code>useState</code>는 서버에서 값을 받아오거나 사용자로부터 입력을 받는 경우(onChange마다 변경이 일어남)에 사용한다.</p>
<h3 id="-usememo-reactmemo">+ useMemo, React.memo</h3>
<p><code>useMemo</code>는 리턴되는 값을 메모이제이션 하는데, 의존성 배열에 있는 값이 바뀌는 경우 호출됩니다.
<code>React.memo</code>는 컴포넌트를 메모이제이션하는데, 컴포넌트의 props가 바뀌지 않으면 리렌더링 하지 않습니다.</p>
<h3 id="리액트에서-메모이제이션을-어떤-방식으로-하나요">리액트에서 메모이제이션을 어떤 방식으로 하나요?</h3>
<p>리액트에서 메모이제이션은 <code>React.memo</code>, <code>useCallback</code>, <code>useMemo</code>를 통해 사용할 수 있습니다.</p>
<p><code>React.memo</code>의 경우 props의 값으로 변경을 확인하고
<code>useCallback</code>과 <code>useMemo</code>는 dependency 배열 내부의 값으로 변경 사항을 확인합니다.</p>
<h3 id="리액트의-렌더링-성능-향상을-위해-어떻게-해야-하나요">리액트의 렌더링 성능 향상을 위해 어떻게 해야 하나요?</h3>
<p><code>useMemo</code>, <code>React.memo</code>, <code>useCallback</code>의 사용,
자식 컴포넌트의 props로 객체를 넘겨줄 때 객체를 변형하거나 새로 생성하지 않고 그대로 넘겨주기, 컴포넌트를 매핑할 때 key값으로 index를 사용하지 않기, <code>useState</code>의 함수형 업데이트 사용, 디바운싱과 쓰로틀링 적용 등이 있습니다.</p>
<h3 id="react-query에-대해-설명해주세요">React-query에 대해 설명해주세요.</h3>
<p><code>React-query</code>는 React 환경에서의 비동기 Query(질의)과정을 도와주는 라이브러리입니다.
<code>React-query</code>는 데이터를 캐싱해서 반복적인 데이터 호출을 방지하고, 서버 데이터와 클라이언트 데이터를 분리하고(클라이언트 데이터는 상태 관리 라이브러리가 관리하고, 서버 데이터는 <code>React-query</code>가 관리), <code>useQuery</code>가 반환하는 <code>isLoading</code>과 <code>error</code>객체를 통해 상황별 분기 처리를 쉽게 진행할 수 있습니다.</p>
<h3 id="react-18-버전-업데이트-내용에-대해-말씀해주세요">React 18 버전 업데이트 내용에 대해 말씀해주세요.</h3>
<p>새로운 Root API(<code>ReactDOM.createRoot</code>)가 적용되었습니다.
React Event Handler뿐만 아니라 promise, setTimeout, native event handler 등 다양한 로직에서 Batching 작업이 가능해졌습니다.
React에서 제공해주는 API를 통해 CRA에서도 SSR을 활용한 아키텍처를 설계할 수 있습니다.
Suspense를 통해 Streaming HTML과 Selective Hydration을 사용할 수 있습니다..</p>
<p><del>어려워서 다시 공부해야 할 듯.</del></p>
<blockquote>
<p><strong>hydration</strong>
정적 호스팅이나 SSR 서버에서 받은 HTML 웹 페이지를 동적 HTML 웹 페이지로 바꿔주는 기술을 말합니다.</p>
</blockquote>
<h3 id="useeffect와-uselayouteffect의-차이점에-대해-말씀해주세요">useEffect와 useLayoutEffect의 차이점에 대해 말씀해주세요.</h3>
<p><code>useEffect</code>는 비동기적으로 렌더링 후에 호출되고, <code>useLayoutEffect</code>는 동기적으로, paint가 되기 전에 실행됩니다. <code>useLayoutEffect</code>는 애니메이션 구현 등 즉시 반응이 필요한 경우나 성능 모니터링의 경우 사용하면 좋습니다.</p>
<h3 id="contextapi에-대해-설명해주세요">ContextAPI에 대해 설명해주세요.</h3>
<p>Context는 리액트 컴포넌트 간에 값을 공유할 수 있게 해주는 기능으로, 주로 전역적으로 필요한 값을 다룰때 사용합니다.</p>
<h3 id="key-props를-사용하는-이유는-무엇인가요">Key Props를 사용하는 이유는 무엇인가요?</h3>
<p>map을 사용할 때 리액트가 key를 통해 어떤 항목을 변경, 추가 삭제할지 식별하기 때문에 엘리멘트의 고유성을 부여하기 위해 key를 지정해야 합니다.</p>
<h3 id="제어-컴포넌트와-비제어-컴포넌트의-차이에-대해-설명해주세요">제어 컴포넌트와 비제어 컴포넌트의 차이에 대해 설명해주세요.</h3>
<p>제어 컴포넌트는 사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트합니다.(<code>setState()</code>사용)
비제어 컴포넌트는 기존의 바닐라 스크립트와 같이 사용자가 직접 트리거 하기 전까지 리렌더링을 발생시키지 않고 값을 동기화 시키지도 않습니다.(<code>ref</code>사용)</p>
<h3 id="props와-state의-차이는-무엇인가요">props와 state의 차이는 무엇인가요?</h3>
<p>props는 부모 컴포넌트에서 상속 받는 데이터로 데이터를 변경할 수 없습니다.
state는 자기 자신의 컴포넌트에서 만들어낸 데이터이며 변경할 수 있습니다.</p>
<h3 id="pure-component에-대해-설명해주세요">pure component에 대해 설명해주세요.</h3>
<p><code>pureComponent</code>는 <code>shouldComponentUpdate()</code>안에 얕은 비교가 적용된 버전입니다.
즉, <code>pureComponent</code>를 사용하면 리액트의 성능을 향상시키는 데 가장 중요한 것 중 하나인 <code>shouldComponentUpdate</code>를 신경쓰지 않아도 됩니다.</p>
<h3 id="shouldcomponentupdate에-대해-설명해주세요">shouldComponentUpdate에 대해 설명해주세요.</h3>
<p><code>shouldComponentUpdate()</code>는 props또는 state가 새로운 값으로 갱신되어 렌더링이 발생하기 직전에 호출됩니다. <code>shouldComponentUpdate()</code>을 사용하면 조건에 따라 데이터가 변경되어 렌더링이 필요한 경우에만 렌더링 작업을 수행할 수 있습니다.</p>
<blockquote>
<p>참조
<a href="https://velog.io/@humonnom/React-useEffect-useMeme-%EA%B7%B8%EB%A6%AC%EA%B3%A0-useCallback-%EA%B0%81-%EC%82%AC%EC%9A%A9%EB%B2%95%EA%B3%BC-%EC%B0%A8%EC%9D%B4">https://velog.io/@humonnom/React-useEffect-useMeme-%EA%B7%B8%EB%A6%AC%EA%B3%A0-useCallback-%EA%B0%81-%EC%82%AC%EC%9A%A9%EB%B2%95%EA%B3%BC-%EC%B0%A8%EC%9D%B4</a>
<a href="https://velog.io/@shin6403/React-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94%ED%95%98%EB%8A%94-7%EA%B0%80%EC%A7%80-%EB%B0%A9%EB%B2%95-Hooks-%EA%B8%B0%EC%A4%80">https://velog.io/@shin6403/React-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94%ED%95%98%EB%8A%94-7%EA%B0%80%EC%A7%80-%EB%B0%A9%EB%B2%95-Hooks-%EA%B8%B0%EC%A4%80</a>
<a href="https://velog.io/@youngminss/React-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0-%EB%A9%94%EC%84%9C%EB%93%9C">https://velog.io/@youngminss/React-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0-%EB%A9%94%EC%84%9C%EB%93%9C</a>
그 외 미처 올리지 못한 출처들</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[오딘 프로젝트] Grid]]></title>
            <link>https://velog.io/@developer-sora/%EC%98%A4%EB%94%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Grid</link>
            <guid>https://velog.io/@developer-sora/%EC%98%A4%EB%94%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Grid</guid>
            <pubDate>Fri, 08 Sep 2023 07:37:23 GMT</pubDate>
            <description><![CDATA[<h2 id="그리드란">그리드란?</h2>
<p>그리드는 레이아웃 도구로써 2차원 배열에서 항목을 쉽게 배치할 수 있다. 하지만 1차원 레이아웃에서도 사용할 수 있다.</p>
<h3 id="flexbox와-grid의-차이">Flexbox와 Grid의 차이</h3>
<p>  * 위 : Flexbox, 아래 : Grid</p>
<ul>
<li>Flexbox는 선택적으로 래핑할 수 있다. 
  Flex 컨테이너의 첫번째 행과 다음 행의 위치는 독립적이므로 벽돌과 같은 모양이 가능하다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/cb15e373-a5b1-414c-b23f-1d761741d4de/image.png" alt=""></p>
<ul>
<li><p>Flexbox는 1차원, Grid는 2차원으로 생각할 수 있다.
<img src="https://velog.velcdn.com/images/developer-sora/post/b915cafb-79d5-4ebf-9be8-84713d7cbfd7/image.png" alt=""></p>
</li>
<li><p>Flexbox는 대부분의 레이아웃이 <strong>자식</strong>에서, Grid는 대부분 <strong>상위 요소</strong>에 발생한다. </p>
</li>
<li><p>Grid는 레이아웃이 겹칠 때 유용하다.(작업이 쉬움)
Flex의 경우 요소가 겹치도록 하려면 음수 여백, absolute 위치 지정과 같은 것이 필요하지만 그리드를 사용하면 그리드 선에 항목을 배치하거나 동일한 그리드 셀 내에 항목을 바로 배치할 수 있다.
<img src="https://velog.velcdn.com/images/developer-sora/post/29be5fd1-53dc-4705-a489-a8e3057ab6c9/image.png" alt=""></p>
</li>
</ul>
<blockquote>
<p>Use <strong>grid</strong> when you already have the <strong>layout structure</strong> in mind, and <strong>flex</strong> when you just want <strong>everything to fit</strong>. 
Layout first vs content first.</p>
</blockquote>
<h2 id="그리드-생성">그리드 생성</h2>
<p>그리드의 정의 방법</p>
<pre><code class="language-css">.contaier {
    display: grid;
    grid-template-columns: 50px 50px
    grid-template-rows: 50px 50px;
}</code></pre>
<p>item1 item2
item3 item4</p>
<p>이렇게 정렬된다.</p>
<p><strong>HTML 요소는 어떻게 그리드 항목이 되나요?</strong></p>
<blockquote>
<p>요소를 그리드 컨테이너로 만들면 됩니다.
<code>display: grid</code></p>
</blockquote>
<p><strong>그리드에 간격은 어떻게 설정합니까?</strong></p>
<blockquote>
<p><code>column-gap</code>및 <code>row-gap</code> 사용, 혹은 단축 속성으로 <code>gap</code>을 사용할 수 있다.</p>
</blockquote>
<p><strong>정의된 트랙보다 콘텐츠가 많으면 어떻게 되는지 설명하세요.</strong></p>
<blockquote>
<p>암시적 그리드 개념으로 인해 새 그리드 트랙을 암시적으로 정의한다.
ex) <code>grid-template:50px 50px / 50px 50px</code>인 경우 5번째 항목이 추가되면 세번째 행에 배치된다.</p>
</blockquote>
<p><strong>정의되지 않은 트랙의 크기를 어떻게 변경할 수 있습니까?</strong></p>
<blockquote>
<p><code>grid-auto-rows</code> 또는 <code>grid-auto-columns</code>속성을 사용하여 추가 콘텐츠에 대해 암시적 그리드가 만드는 새로운 트랙의 크기를 설정할 수 있다.
ex) <code>grid-auto-rows: 50px;</code></p>
</blockquote>
<h2 id="그리드의-요소-위치-지정">그리드의 요소 위치 지정</h2>
<p><strong>그리드 위치를 지정하는 방법</strong></p>
<pre><code class="language-css">.container {
    display: inline-grid;
    grid-template: 40px 40px 40px 40px 40px / 40px 40px 40px 40px;
    background-color: lightblue;
 }

 .room {
     border: 1px solid;
       font-size: 50%;
    text-align:center;
 }

 #living-room {
     grid-column-start: 1;
    grid-column-end: 6;   // 6칸의 열(가로) 차지
    grid-row-start: 1;
    grid-row-end: 3;      // 3칸의 행(세로) 차지
 }

#bedroom {
  grid-column-start: 1;
  grid-column-end: 3;
  grid-row-start: 3;
  grid-row-end: 5;
}

#closet {
  grid-column-start: 1;
  grid-column-end: 3;
  grid-row-start: 5;
  grid-row-end: 6;
}

#bathroom {
  grid-column-start: 3;
  grid-column-end: 4;
  grid-row-start: 3;
  grid-row-end: 6;
}

/* 단축 속성 사용 가능*/
 #kitchen {
     grid-column: 4 / 6;  // 4칸~6칸의 열 차지
    grid-row: 3 / 6;     // 3칸~6칸의 행 차지
 }
</code></pre>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/742273f2-39b4-4d85-90b7-f5cf673ad3b1/image.png" alt=""></p>
<p><strong>그리드 라인과 트랙, 영역은 무엇인가요?</strong></p>
<blockquote>
</blockquote>
<ul>
<li>*<em>그리드 라인 *</em>
그리드의 구조를 구분하는 구분선. 그리드 항목을 배치하는데 사용된다.
<img src="https://velog.velcdn.com/images/developer-sora/post/b12072e7-07f6-4bc5-ad28-49758a1aea65/image.png" alt=""></li>
<li><strong>그리드 트랙</strong>
  인접한 두 그리드 선 사이의 공간. 그리드의 열이나 행
  <img src="https://velog.velcdn.com/images/developer-sora/post/4b266eea-67ff-4ceb-82ae-f8b325fe2b22/image.png" alt=""></li>
<li><strong>그리드 영역</strong>
4개의 그리드 선으로 둘러싸인 전체 공간
<img src="https://velog.velcdn.com/images/developer-sora/post/7d9db992-6b28-4559-b598-5ab2941fde3e/image.png" alt=""></li>
<li><strong>그리드 셀</strong>
그리드의 단일 &quot;단위&quot;
<img src="https://velog.velcdn.com/images/developer-sora/post/729ca5f9-b3a0-4bec-bf0a-b6cfaf5fd4c7/image.png" alt=""></li>
</ul>
<p><strong>grid area란?</strong></p>
<blockquote>
<p><code>grid-row-start</code>/<code>grid-column-start</code>/<code>grid-row-end</code>/<code>grid-column-end</code>을 한줄로 결합할 수 있다.</p>
</blockquote>
<pre><code class="language-css">#living-room {
 grid-area: 1 / 1 / 3 / 6
}</code></pre>
<p><strong>grid의 시각적 레이아웃을 단어로 만드는 방법?</strong></p>
<blockquote>
<pre><code class="language-css">.container {
    display:inline-grid;
    ...
    grid-template-areas:
    &quot;living-room living-room living-room . .&quot; 
}  /* .를 넣으면 빈 셀을 표시할 수 있다. */
</code></pre>
</blockquote>
<p>#living-room {
    grid-area: living-room;
}</p>
<h2 id="고급-그리드-속성">고급 그리드 속성</h2>
<p>그리드의 반응성을 높이고, 동적이고 재사용 가능하게 만드는 방법</p>
<p>** 동일한 크기의 여러 그리드 트랙을 만드는 방법?**</p>
<blockquote>
<p><code>repeat()</code>을 사용한다.</p>
</blockquote>
<pre><code class="language-css">.grid-container {
    grid-template-rows: 150px 150px;
    grid-template-columns: 150px 150px 150px
 }</code></pre>
<p>다음과 같이 작성할 수 있다.</p>
<pre><code class="language-css">.grid-container {
    grid-template-rows: repeat(2,150px);
    grid-template-columns: repeat(3,150px);
 }</code></pre>
<p><strong>정적 크기 값과 동적 크기 값의 차이점?</strong></p>
<blockquote>
<p>정적 크기는 <code>px</code>,동적 크기는 분수 단위인 <code>fr</code>이 있다.
<code>fr</code>단위는 그리드에 남아 있는 공간을 분배하는 방법이다.<br>총 너비 <code>400px</code>인 4개의 열의 경우 <code>1fr</code>은 <code>100px</code>가 된다.</p>
</blockquote>
<p>** 그리드의 나머지 공간이 고르지 않게 분포되도록 그리드 트랙을 할당하려면 어떻게 해야합니까?**</p>
<blockquote>
<p>처음 두 개의 열에 <code>2fr</code>의 크기를 지정하고 나머지 세 개의 열에 <code>1fr</code>의 크기를 지정하려면
<code>grid-template-columns: repeat(2, 2fr) repeat(3, 1fr)</code>
이렇게 작성하면 된다.</p>
</blockquote>
<p><strong>실시간으로 계산되는 최소 및 최대 트랙 크기를 제공할 수 있는 CSS 그리드 전용 기능은 무엇입니까?</strong></p>
<blockquote>
<p><code>minmax()</code> 함수. <code>minmax(150px, 200px)</code>는 최소 150px, 최대 200px를 의미한다.</p>
</blockquote>
<p><strong>실시간으로 계산되는 최솟값,이상값,최댓값을 제공할 수 있는 전역 CSS함수는 무엇입니까?</strong></p>
<blockquote>
<p><code>clamp(minimun-size, ideal-size, maximum-size)</code></p>
</blockquote>
<p>** 특정 제약 조건이 주어지면 가능한 많은 그리드 트랙을 채우는데 사용할 수 있는 <code>repeat()</code>의 속성은 무엇입니까?**</p>
<blockquote>
<p><code>auto-fit</code>, <code>auto-fill</code>
두 함수는 모두 그리드 항목이 컨테이너를 넘치지 않고 &quot;가능한 가장 큰 양의 정수&quot;를 반환한다.</p>
</blockquote>
<p><strong><code>auto-fit</code>과 <code>auto-fill</code>의 차이점은 무엇인가요?</strong></p>
<blockquote>
<p><code>auto-fill</code>은 <code>auto-fit</code>과 거의 같은 방식으로 작동하지만 차이점은 그리드 행 전체를 다 채울 수 있는 것 보다 항목이 적은 경우에 있다.</p>
</blockquote>
<ul>
<li><code>auto-fit</code>은 다른 그리드 항목이 들어갈 수 있는 크기로 확장되어지만 남은 항목이 없는 경우 그리드 항목을 <code>max</code>크기로 유지하고,</li>
<li><code>auto-fill</code>은 렌더링할 항목이 없더라도 다른 그리드 항목을 추가할 수 있는 공간이 확보되면 그리드 항목이 해당 크기로 다시 축소된다.<blockquote>
<p>ex) <code>grid-template-columns:{ repeat(auto-fit, minmax(150px, 1fr));}</code>
<img src="https://velog.velcdn.com/images/developer-sora/post/de436a09-7990-47c8-b940-6f9b9622a163/image.png" alt=""></p>
<p>ex) <code>grid-template-columns:{ repeat(auto-fill, minmax(150px, 1fr));}</code>
<img src="https://velog.velcdn.com/images/developer-sora/post/ff1aa03d-1297-4936-90bb-7f15afb59259/image.png" alt=""></p>
</blockquote>
</li>
</ul>
<h3 id="오딘-프로젝트-과제">오딘 프로젝트 과제</h3>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/efc8451b-4e98-4627-b728-58a1197b0a8c/image.png" alt=""></p>
<p>로고 왼쪽 정렬 <code>justify-self: start</code>;
헤더 메뉴 글자 가운데 정렬 <code>align-self: center</code></p>
<blockquote>
<p>참조
<a href="https://css-tricks.com/snippets/css/complete-guide-grid/">https://css-tricks.com/snippets/css/complete-guide-grid/</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[오딘 프로젝트] Forms + 프로젝트]]></title>
            <link>https://velog.io/@developer-sora/%EC%98%A4%EB%94%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Forms</link>
            <guid>https://velog.io/@developer-sora/%EC%98%A4%EB%94%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Forms</guid>
            <pubDate>Mon, 28 Aug 2023 05:46:30 GMT</pubDate>
            <description><![CDATA[<h2 id="form-basics">Form Basics</h2>
<blockquote>
<p>form 요소의 목적과 항상 포함해야 하는 두 가지 속성을 설명하십시오.</p>
</blockquote>
<p> form 요소는 사용자가 양식에서 상호 작용할 모든 입력을 래핑합니다.
 form 요소는 두 가지 필수 속성을 허용합니다. </p>
<ul>
<li><code>action</code> : 처리할 데이터를 어디로 보내는지 알려주는 URL 값</li>
<li><code>method</code> : form을 제출하기 위해 사용하는 <code>HTTP</code> 요청 방법</li>
</ul>
<pre><code class="language-html">&lt;form action=&quot;example.com/path&quot; method=&quot;post&quot;&gt;

&lt;/form&gt;</code></pre>
<blockquote>
<p><code>&lt;label&gt;</code>은 무엇입니까?</p>
</blockquote>
<p>사용자가 입력할 데이터 유형을 알려주는 레이블입니다.</p>
<p>라벨의 <code>for</code> 속성은 레이블과 form 컨트롤 요소에 <code>id</code> 속성과 동일한 값을 가지면 라벨과 요소가 연결됩니다.</p>
<pre><code class="language-html">&lt;form action=&quot;example.com/path&quot; method=&quot;post&quot;&gt;
  &lt;label for=&quot;first_name&quot;&gt;First Name:&lt;/label&gt;
  &lt;input type=&quot;text&quot; id=&quot;first_name&quot;&gt;
&lt;/form&gt;</code></pre>
<blockquote>
<p>높은 수준의 form 컨트롤이 무엇인지 설명하십시오.</p>
</blockquote>
<p><code>textboxes</code>, <code>dropdowns</code>, <code>checkboxes</code>, <code>buttons</code></p>
<blockquote>
<p><code>name</code> 속성은 무엇입니까?</p>
</blockquote>
<p>백엔드에 데이터를 보낼 때 입력된 데이터 조각이 무엇을 나타내는지 설명하는 것입니다.</p>
<p><code>name</code>속성은 form 컨트롤에 입력된 데이터의 참조 역할을 하기 때문에 form 입력에는 항상 <code>name</code>속성이 있어야합니다.  </p>
<blockquote>
<p>사용자가 미리 정의된 옵션을 선택할 수 있도록 하는 데 사용할 수 있는 가장 일반적인 세 가지 양식 컨트롤은 무엇입니까?</p>
</blockquote>
<p><code>select</code>, <code>radio button</code>, <code>checkbox</code></p>
<blockquote>
<p>HTML의 세 가지 유형의 버튼은 무엇입니까?</p>
</blockquote>
<p><code>type=&quot;submit&quot;</code>, <code>type=&quot;reset&quot;</code>, <code>type=&quot;button&quot;</code></p>
<p>버튼의 기본 유형은 <code>submit</code>이기 때문에 form과 관련 없는 버튼을 누른 경우 submit이 될 가능성이 있기 때문에 <strong>항상 type을 명시해주는 것이 중요</strong>하다.  </p>
<blockquote>
<p>form 요소 구성은 무엇이 있습니까?</p>
</blockquote>
<p><code>fieldset</code>는 관련 form 요소를 하나의 논리 단위로 <em>그룹화</em> 하는 컨테이너 입니다.</p>
<p><code>legend</code> 는 <code>fieldset</code>의 _제목이나 캡션을 제공_하는데 사용됩니다.</p>
<blockquote>
<p>스타일링 양식에서 가장 어려운 두 가지 측면은 무엇입니까?</p>
</blockquote>
<ol>
<li><p>각 브라우저는 form 컨트롤에 대해 고유한 기본 스타일이 있기 때문에 모든 브라우저에서 일관된 디자인을 유지하려면 기본 스타일을 <strong>재정의</strong>하고 직접 스타일을 지정해야 합니다.</p>
</li>
<li><p>라디오 버튼과 체크박스에 사용자 정의 스타일을 만드는 것은 까다롭습니다. 하지만 <a href="https://moderncss.dev/pure-css-custom-checkbox-style/">스타일링 가이드</a>와 최근에는 라디오 버튼과 체크박스의 스타일을 쉽게 지정할 수 있는 <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/accent-color">새로운 CSS 속성</a> (accent-color)도 제공되었습니다.</p>
<p>달력이나 날짜 선택기와 같은 다른 요소의 특정 측면은 스타일을 지정하는 것이 불가능하기 때문에  Javascript로 사용자 정의 form 컨트롤을 구축하거나 Javascript 라이브러리 중 하나를 사용해야 합니다.</p>
</li>
</ol>
<h2 id="form-validation">Form Validation</h2>
<blockquote>
<p><code>required</code>는 무엇을 합니까?</p>
</blockquote>
<p>필드의 입력을 필수로 만들 수 있다.
<em>ps.필드 레이블에 (*****)를 추가하면 좋다.</em></p>
<blockquote>
<p>텍스트 길이를 확인하기 위해 어떤 검증을 사용할 수 있나요?</p>
</blockquote>
<p><code>minlength</code>, <code>maxlength</code></p>
<blockquote>
<p>숫자 입력의 최소값과 최대값을 어떻게 검증할 수 있나요?</p>
</blockquote>
<p><code>min</code>, <code>max</code></p>
<blockquote>
<p>패턴 검증을 어떤 용도로 사용할 수 있나요?</p>
</blockquote>
<p>데이터가 특정 패턴과 일치하는지 확인
ex) 신용카드 번호, 우편번호 등</p>
<p><code>pattern</code> 속성에 정규식을 넣어서 패턴 유효성 검사를 할 수 있다.</p>
<pre><code class="language-html">&lt;input type=&quot;text&quot; id=&quot;zip_code&quot; name=&quot;zip_code&quot; pattern=&quot;(\d{5}([\-]\d{4})?)&quot; title=&quot;Please enter a valid zip code, example: 65251&quot; required&gt;</code></pre>
<p><code>title</code> 속성으로 경고 메시지를 커스텀 할 수 있다.</p>
<blockquote>
<p>유효한 입력과 유효하지 않은 입력의 스타일을 지정하는 데 사용할 수 있는 의사 CSS 선택자는 무엇입니까?</p>
</blockquote>
<p><code>:valid</code>, <code>:invalid</code></p>
<p><img src="https://velog.velcdn.com/images/developer-sora/post/a26ad976-15d8-4523-8335-49754cd6e86e/image.png" alt=""></p>
<p>간단하게 입력폼이랑 html 유효성검사 적용한 회원가입 화면 만들어봤다. 이쁘게 만들고 싶었지만 나의 한계.. 🫠 갓기들 미안해</p>
<p>자바스크립트 유효성 검사는 자바스크립트 파트 들어가면 적용해야겠다.</p>
<blockquote>
<p>오딘 프로젝트 링크<br><a href="https://www.theodinproject.com/lessons/node-path-intermediate-html-and-css-form-basics#a-note-on-styling-forms">https://www.theodinproject.com/lessons/node-path-intermediate-html-and-css-form-basics#a-note-on-styling-forms</a></p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>