<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jiiker.log</title>
        <link>https://velog.io/</link>
        <description>Hello, world!</description>
        <lastBuildDate>Sat, 28 Mar 2026 22:45:07 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jiiker.log</title>
            <url>https://velog.velcdn.com/images/jiiker_/profile/22ffb021-f607-45fc-a441-0a0c673129bf/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jiiker.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jiiker_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[토스 Frontend Fundamentals 모의고사 2회차 후기]]></title>
            <link>https://velog.io/@jiiker_/%ED%86%A0%EC%8A%A4-Frontend-Fundamentals-%EB%AA%A8%EC%9D%98%EA%B3%A0%EC%82%AC-2%ED%9A%8C%EC%B0%A8-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@jiiker_/%ED%86%A0%EC%8A%A4-Frontend-Fundamentals-%EB%AA%A8%EC%9D%98%EA%B3%A0%EC%82%AC-2%ED%9A%8C%EC%B0%A8-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Sat, 28 Mar 2026 22:45:07 GMT</pubDate>
            <description><![CDATA[<p>일을 시작하고 나서 바쁘다는 핑계로 벨로그, 공부 등등 하나 둘 놓다보니 어느 순간 두려움이 엄습해왔다. 빠르게 성장해서 스펙을 쌓아가고 싶은 마음은 굴뚝 같은데, 어디서부터 뭘 해야할지 막막했다. 그 와중에 감사하게도 지인분이 토스 모의고사 2회차 진행 소식을 알려주셨고, 링크를 전달받아 참여하게 되었다.</p>
<p>토스 모의고사에 참여하게 된 이유로 <strong>성장에 대한 병목</strong>을 해소하고 싶었던 것도 있지만, 막연하게 자리하고 있던 <strong>토스 과제에 대한 두려움을 떨쳐버리고 싶었던 이유</strong>도 있었다. 이 모든 것들을 <strong>걱정하면서도 아무것도 하지 않던 나태함을 어떻게든 극복</strong>하고자 참여했다.</p>
<p><img src="https://velog.velcdn.com/images/jiiker_/post/7f459940-9952-49b1-ae50-c24735326772/image.jpg" alt=""></p>
<h1 id="2회차-과제---회의실-예약">2회차 과제 - 회의실 예약</h1>
<h2 id="과제-내용">과제 내용</h2>
<p>과제의 내용은 생각보다 단순했다. 이미 기능이 동작하도록 구현되어있는 회의실 예약 페이지 코드를 리팩토링 하는 과제였다.</p>
<blockquote>
<p><em><strong>&quot;서비스의 유지보수나 장기적인 확장성을 고려한 설계, 추상화 관점에 집중해서 기능을 구현해주세요.&quot;</strong></em></p>
</blockquote>
<h2 id="과제-수행">과제 수행</h2>
<p>그런데 생각보다 단순한 과제에 비해 나는 어떻게 시작해야할지 전혀 감이 안왔다... 이전에 어떤 코드를 <strong>체계적으로 리팩토링 해 본 경험이 없었</strong>을 뿐만 아니라 &#39;토스 개발자들은 어떤 참신한 고민들을 할까..?&#39; 싶은 생각에 <strong>나의 보잘 것 없어 보이는 고민들을 드러내는 것이 부끄러웠다.</strong></p>
<p>그래서 일단 1회차 과제 제출물들을 살펴보고, 1회차 과제 후기들을 보며 공부부터 시작했다. 그 중 자주 나타났던 키워드는 <strong>추상화</strong>와 <strong>추출</strong>이었고, 이 두 개념을 구분하고 &#39;추상화&#39;가 무엇인지에 대해 고민을 해봤다. 자료들을 찾아가며 어떤 부분을 <strong>추상화할 수 있는 특별한 포인트가 없을지 고민</strong>해봤지만 큰 수확 없이 시간만 속절없이 흘러갔다. 아무래도 <strong>추상화에 대한 스스로의 명확한 기준이 없다보니</strong> 다 맞는 말처럼 느껴져서 방향성을 제대로 잡지 못 했던 것 같다.</p>
<p>정말 이러다가는 시간 내에 아무것도 못 할 것 같다는 생각이 들 때 쯤에 방향성을 변경했다. 우선 <strong>내가 기존에 알고 있던 &#39;추상화&#39;는 무엇</strong>일까 생각해보고 이를 토대로 리팩토링을 진행하기로 했다. 일단 뭐라도 해보고 회초리 맞으며 배워야겠다고 생각했다.</p>
<p>그렇게 크게 세 가지 기준에서 리팩토링을 진행했다.</p>
<blockquote>
<ul>
<li><em><strong>확장성과 유지보수</strong></em></li>
<li><em><strong>인지 부하 해소</strong></em></li>
<li><em><strong>구현 세부사항 은닉</strong></em></li>
</ul>
</blockquote>
<p>이 기준들과 함께 <strong>&#39;읽기 쉬운 코드&#39;</strong>를 작성하는 것을 목표로 작업을 시작했다.</p>
<h3 id="폴더-구조">폴더 구조</h3>
<p>우선은 <strong>폴더 구조를 최대한 단순하게</strong> 잡았다. 요즘 핫한 FSD 아키텍처와 관련해서는 깊이 있게 알고 있지도 않았고, 쓸데없이 화려한 느낌이 있어서 사용하지는 않았다.</p>
<pre><code class="language-bash">RoomBookingPage/          # 회의실 예약 페이지
├── index.tsx
├── components/           # 이 페이지에서만 사용하는 컴포넌트
├── hooks/                # 이 페이지에서만 사용하는 훅
├── constants/            # 이 페이지에서만 사용하는 상수
└── utils/                # 이 페이지에서만 사용하는 순수 함수</code></pre>
<p>각 페이지별로 <strong>기능 단위로 최소한의 폴더</strong>로만 구성했고, <strong>공통 코드는 <code>/shared</code> 디렉토리로 분리</strong>했다.</p>
<h3 id="컴포넌트-및-함수-분리">컴포넌트 및 함수 분리</h3>
<pre><code class="language-js">export function ReservationStatusPage() {
  ...

  const { data: rooms = [] } = useQuery(getRoomsQueryOptions());
  const { data: reservations = [] } = useQuery(getReservationsQueryOptions(date));
  const { data: myReservationList = [] } = useQuery(getMyReservationsQueryOptions());

  ...

  const handleCancel = async (id: string) =&gt; {
    try {
      await cancelReservation(id);
      setMessage({ type: &#39;success&#39;, text: &#39;예약이 취소되었습니다.&#39; });
    } catch {
      setMessage({ type: &#39;error&#39;, text: &#39;취소에 실패했습니다.&#39; });
    }
  };

  return (
      ...

      &lt;Spacing size={24} /&gt;
      &lt;Border size={8} /&gt;
      &lt;Spacing size={24} /&gt;

      {/* 예약 현황 타임라인 */}
      &lt;div css={css`padding: 0 24px;`}&gt;
        &lt;Text typography=&quot;t5&quot; fontWeight=&quot;bold&quot; color={colors.grey900}&gt;
          예약 현황
        &lt;/Text&gt;
        &lt;Spacing size={16} /&gt;
        &lt;ReservationTimeline rooms={rooms} reservations={reservations} /&gt;
      &lt;/div&gt;

      &lt;Spacing size={24} /&gt;
      &lt;Border size={8} /&gt;
      &lt;Spacing size={24} /&gt;

      ...
  );
}</code></pre>
<p>컴포넌트 분리에 있어서는 최대한 <strong>페이지단에서 데이터 호출이나 핸들러 함수들을 관리</strong>하고, <strong>하위 컴포넌트는 순수성을 유지</strong>하려고 했다. 그리고 그 과정에서 <strong>페이지 컴포넌트만 보더라도 데이터의 흐름이 한 눈에 파악</strong>될 수 있도록 <strong>인지 부하를 유발하는 요소들을 유틸 함수나 훅으로 분리</strong>했다.</p>
<h3 id="쿼리-옵션-분리">쿼리 옵션 분리</h3>
<pre><code class="language-js">// src/ReservationStatusPage/index.tsx

const { data: rooms = [] } = useQuery(getRoomsQueryOptions());
const { data: reservations = [] } = useQuery(getReservationsQueryOptions(date));
const { data: myReservationList = [] } = useQuery(getMyReservationsQueryOptions());



// src/shared/queries/reservation.ts

export const ROOMS_QUERY_KEY = [&#39;rooms&#39;] as const;
export const RESERVATIONS_QUERY_KEY = [&#39;reservations&#39;] as const;

export function getRoomsQueryOptions() {
  return {
    queryKey: ROOMS_QUERY_KEY,
    queryFn: getRooms,
  };
}

export function getReservationsQueryOptions(date: string) {
  return {
    queryKey: [...RESERVATIONS_QUERY_KEY, date] as const,
    queryFn: () =&gt; getReservations(date),
    enabled: !!date,
  };
}</code></pre>
<p>쿼리 옵션을 함수로 추상화하여 <strong>호출부에서는 <code>queryKey</code>와 <code>queryFn</code>의 세부사항을 몰라도 되도록</strong> 했다. 또한 <strong>유지보수성</strong>을 고려해 하드코딩된 <strong>쿼리키를 상수로 분리했다.</strong> 특히, <code>RESERVATIONS_QUERY_KEY</code>의 경우 예약 취소 로직에서 해당 키를 통해 <code>invalidateQueries()</code> 처리를 해줘야 했기 때문에 상수로 분리하는 것이 좋다고 판단했다.</p>
<p>크게 이 정도 틀에서 작업들을 수행했고, 어느덧 제출 마감시간이 되어 서둘러 마무리했다. 돌이켜보니 이것저것 공부했던 것에 비해서 너무 단순 코드 추출 작업만을 하다 과제를 마무리한 것 같아서 많은 아쉬움이 남았다. 다음번에도 기회가 된다면 좀 더 여유롭게 과제를 시작해서 깊이 있는 고민을 하고 다른 분들과 토론을 나눠볼 수 있으면 좋을 것 같다.</p>
<h1 id="해설-강의">해설 강의</h1>
<p>해설강의는 총 2부로 나누어 진행되었고, <strong>오종택님</strong>이 진행을, <strong>한재엽님</strong>과 <strong>문동욱님</strong>이 각각 1부와 2부에서 해설 및 코드 리뷰를 진행해 주셨다. 결론부터 얘기를 하자면, 해설강의를 듣고서 정말 많은 생각이 들었다. 특히, 나 스스로 &#39;토스&#39;에 대해 실체 없는 벽을 세워두고 있었다는 것을 깨달았다. &#39;끊임없이 참신한 것들을 만들어 내는 곳&#39;, &#39;토양어선이래...&#39;, &#39;코딩 괴물들만 가는 곳&#39; 등등... </p>
<p>해설 강의를 들으며 깨달은 것은 이번 모의고사에서 <strong>&#39;얼마나 참신한 고민을 하는 것&#39;은 전혀 중요한 것이 아니었다.</strong> <strong>&#39;당연한 것을 당연하지 않게 생각하는 것&#39;이 정말 중요</strong>하다는 것을 느꼈다. 코드를 작성함에 있어서 내 기준이 없었던 것은 <strong>&#39;당연한 것을 너무도 당연하게 생각&#39;</strong>하고, 어떤 선택지에서 뭐가 더 좋을지 고민하기 보단 <strong>&#39;둘 다 괜찮은데?&#39;라며 가벼이 넘겨 버렸기 때문</strong>이 아닐까 싶다. 내가 당연하게 넘겼던 그 <strong>사소한 부분들에서 디테일한 차이를 만들어 내고 그것들이 모여 참신함을 만들어내는 사람들이 모인 곳</strong>이 토스라는 생각이 들었다.</p>
<p>해설 강의 내용에 대해서는 간략하게 인상깊었던 부분 위주로 남겨 본다.</p>
<h2 id="1부---이상적인-인터페이스-설계부터">1부 - 이상적인 인터페이스 설계부터!</h2>
<p>어찌보면 너무 당연한 말일수도 있는데, 이미 짜여진 코드를 리팩토링 하라고 했을 때 빈 파일부터 열었던 적은 단 한 번도 없는 것 같다. 재엽님의 해설 강의 시작은 <strong>빈 파일에서 이상적인 인터페이스를 설계하는 것부터 시작</strong>됐다. 이 과정이 <strong>기존 코드를 먼저 봤을 때 알게 모르게 생기는 미련(?)을 버리는 데 도움</strong>이 된다고 하셨다. </p>
<p>이 부분이 공감이 됐던 게 실제로 어떤 코드를 보면 &#39;다시 짜는 게 낫겠는데?&#39; 싶은 코드들이 있다. 보통 이 경우에 생각은 이렇게 하더라도 &#39;이 복잡하고 지저분한 걸 어떻게 리팩토링 해?&#39;를 돌려 말한 것이지 실제로 다시 짜려고 했던 생각은 아니었다... 이게 어찌보면 내 스스로가 <strong>리팩토링에 대해서 &#39;기존 코드에서 시작한다&#39;는 선입견을 가지고 있었던 것이 아닐까</strong> 하는 생각이 들었다.</p>
<h3 id="추상화와-추출의-차이">추상화와 추출의 차이</h3>
<p>그래서 이게 뭔데! 이제는 나도 좀 알자! 재엽님이 추상화와 추출의 차이에 대해서 예시를 들어 설명해주셨는데, 내가 이해한 바로 결론을 내려보자면, <strong>추출은 &#39;있는 그대로&#39; 코드를 덜어내는 작업</strong>이고, <strong>추상화는 코드를 덜어내면서 &#39;최대한의 재사용성을 확보&#39;하는 작업</strong>으로 이해했다.</p>
<p>예시는 회의실 예약 화면에서 몇 층의 회의실을 선택할 것인지 고르는 <strong><code>Select</code> 컴포넌트</strong>를 예로 드셨다. 이 Select 컴포넌트는 <strong>단순히 1~6 사이의 숫자를 고르게</strong> 되어있다. 이 경우에 이 컴포넌트를 <strong><code>FloorSelect</code> 컴포넌트</strong>로 분리한다면, 단순히 숫자를 고르는 컴포넌트임에도 불구하고 컴포넌트명으로 인해 층 수 선택에만 사용할 수 밖에 없게 된다. 이 때, <strong><code>NumberSelect</code>로 분리하게 되면 컴포넌트의 기능에 핏(fit)한 컴포넌트명을 갖게 되면서 재사용성이 확보</strong>된다. 그렇다고 해서 무조건 FloorSelect로 구분하는 것이 잘못된 것은 아니다. 예를 들어 L(로비층), F(4층) 등 숫자가 아닌 선택지가 Select 리스트에 포함되는 경우에는 FloorSelect가 좋은 추상화가 될 수도 있다. </p>
<h2 id="2부---우리는-코드를-예측한다">2부 - 우리는 코드를 &quot;예측&quot;한다.</h2>
<p>우리가 코드를 읽을 때 단순히 그 부분만을 읽는 것이 아니고, <strong>뒤에 따라올 내용을 예측하며 읽게 된다.</strong> 그런데 예측이 빗나가게 되면, 다시 생각하게 되고, 그런 것들이 반복되면 이해하기 어려운 코드가 된다. 반대로, <strong>예측하기 쉬운 코드가 이해하기 쉬운 코드</strong>가 되는 것이다.</p>
<h3 id="감춰야-하는-관심사-vs-알아야-하는-관심사">감춰야 하는 관심사 vs 알아야 하는 관심사</h3>
<p>동욱님이 몇 가지 예시를 들어 설명해주셨다. 예를 들어 <strong>컴포넌트 하단에 Spacing</strong>이 들어가 있는 경우 이 <strong>여백(Spacing)은 컴포넌트의 기능과 관련이 없다.</strong> 따라서 이런 코드들은 컴포넌트 <strong>외부에 위치</strong>하는 것이 적절하다. </p>
<p>또 함수(컴포넌트)의 <strong>인터페이스는 해당 함수의 연결부</strong>이다. 그래서 함수의 <strong>인자들은 내부를 바라보고 있어야</strong> 하고, <strong>호출부의 맥락에 관심을 가지는 순간 재사용성이 현저하게 떨어지게</strong> 된다. 예를 들어서 날짜를 선택하는 기능이 있는 <code>DataSelector</code> 컴포넌트에서 <code>setDate</code>를 인자로 받아서 <code>onChange</code>에 전달 해주고 있을 때, 컴포넌트의 props로 <code>setDate={setDate}</code>로 받는 것보다는 <strong><code>onChange={setDate}</code>로 받는 것</strong>이 더 적절하다.</p>
<p>한편 동욱님은 이렇게 해설 강의를 진행했지만, 이것이 <strong>완전한 정답은 아니며 항상 의심하고 다시 생각해볼 것을 당부</strong>하셨다.</p>
<h1 id="후기">후기</h1>
<p>항상 개발 과제들을 진행하면서 <strong>&#39;나의 특별함을 어떻게 보여주지?&#39;</strong>라는 생각을 했던 것 같습니다. 이번 과제 또한 처음에 &#39;추상화&#39;에 대해서 학습한 뒤, 남들이 발견 못 한 어떤 부분을 기가막힌 방법으로 추상화할 수 있지 않을까하는 생각으로 접근했던 것 같습니다.</p>
<p>하지만, 이번 과제를 통해 느낀점은 <strong>&#39;추상화&#39;라는 것</strong>은 특별하고 특출난 기법에 의미가 있는 것이 아니고, <strong>지극히도 보편적인 사고에 기반하여, 모두가 공감할 수 있는, 어떤 가려운 부분을 긁어주는 것</strong>에 가까운 작업이라고 느꼈습니다. 그렇기 때문에 영원한 정답이 있는 것도 아니고, <strong>앞으로 개발을 하면서 계속해서 제 스스로 이 기준을 만들고 수정해 나가야겠다</strong>고 생각했습니다.</p>
<p>마지막으로 좋은 기회를 마련해주신 종택님, 재엽님, 동욱님께 감사하다는 말씀 드리며 글을 마치겠습니다! 🙇</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[태양을 피하고 싶어서 만든 앱]]></title>
            <link>https://velog.io/@jiiker_/%ED%83%9C%EC%96%91%EC%9D%84-%ED%94%BC%ED%95%98%EA%B3%A0-%EC%8B%B6%EC%96%B4%EC%84%9C-%EB%A7%8C%EB%93%A0-%EC%95%B1</link>
            <guid>https://velog.io/@jiiker_/%ED%83%9C%EC%96%91%EC%9D%84-%ED%94%BC%ED%95%98%EA%B3%A0-%EC%8B%B6%EC%96%B4%EC%84%9C-%EB%A7%8C%EB%93%A0-%EC%95%B1</guid>
            <pubDate>Sat, 12 Jul 2025 21:04:53 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>배포중인 URL: <a href="https://project-g-seven.vercel.app/" target="_blank">https://project-g-seven.vercel.app/</a></p>
</blockquote>
<h1 id="프로젝트-개요">프로젝트 개요</h1>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/151cb76d-31de-405a-b162-d9ae07229585/image.png" width="70%"/>
<p/>

<p>날씨가 너무 덥다... 깜빡하고 선크림이라도 안 바른 날에는 태양빛에 닿으면 죽는 뱀파이어 마냥 그늘을 찾아 다닌다. 우리는 어렴풋이 해는 동쪽에서 떠서 서쪽으로 진다고 알고있지만, 중위도 지역에 위치한 <strong>대한민국은 다양한 계절만큼이나 태양의 위치도 다양</strong>하다. 여름은 북동쪽에서 북서쪽, 겨울은 남동쪽에서 남서쪽, 봄/가을에는 동쪽에서 서쪽으로 계절별로 조금씩 차이가 있다. 나름 지구과학 과목에서 1등급을 받았었지만, &#39;북반구 중위도 지역의 여름이니까 오늘은 태양이 북동쪽에서 북서쪽으로 지겠구나.&#39; 따위의 생각은 하지 않는다. 그리고 이제 나름 개발자로 전직하지 않았는가. 그래서 만들어버렸다. <strong>지도에 그늘을 표시해주는 앱</strong>. 일명 <strong>그늘길 프로젝트</strong>.</p>
<h1 id="아이디어">아이디어</h1>
<p>아이디어의 시작은 <strong>건물 부지와 건물의 높이만 알면 대략적인 그림자를 그릴 수 있지 않을까</strong>하는 생각에서 출발했다. 그리고 건물 부지와 건물 높이는 여러가지 이유들로 인해 반드시 데이터로 관리될 것 같아서 어떻게든 얻을 수 있는 데이터라고 생각이 들었다. 우선 지도 API도 여러 가지가 있었고, 건물 데이터는 국토교통부에서 제공하는 <a href="https://www.data.go.kr/data/15123970/openapi.do">공공데이터 API</a>도 있었다. 그런데 지도 API 중에 3D 건물 데이터를 제공해주는 API가 있었다. <a href="https://www.mapbox.com/"><strong>Mapbox API</strong></a>인데, <strong>지도와 건물 데이터 간에 호환성이 가장 좋을 것 같아서 일단 프로토타입을 이 API로 만들어 보기로 결정</strong>했다.</p>
<h2 id="그림자를-어떻게-그릴-것인가">그림자를 어떻게 그릴 것인가?</h2>
<blockquote>
<ol>
<li><strong>모든 건물은</strong> 밑면부터 천장까지 일정한 모양을 가지는 <strong>기둥 형태</strong>라고 가정</li>
<li><strong>태양 빛은</strong> 태양 고도의 각도로 <strong>평행하게 입사</strong>한다고 가정</li>
</ol>
</blockquote>
<h2 id="그림자-길이">그림자 길이</h2>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/c8f0580d-7535-4259-a9a5-d7ba087fd30e/image.PNG" width="70%"/>
<p/>

<p><strong>그림자의 길이</strong>는 간단한 <strong>삼각함수 계산</strong>을 통해 구할 수 있다.</p>
<h2 id="그림자-모양">그림자 모양</h2>
<p>그림자 모양을 어떻게 결정해야될지 고민을 많이 했다. 일단 <strong>굉장히 가벼운 앱으로 만들고 싶었기 때문</strong>에 로직을 복잡하게 가져가고 싶지 않았고, 아주 <strong>단순한 계산으로 그리고 싶었다.</strong></p>
<h3 id="방법1-turfjs---convex-메서드">[방법1] Turf.js - convex 메서드</h3>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/afe732a2-0593-4d81-85df-094bd9eb0988/image.png" width="70%"/>
<p/>

<p>자바스크립트 라이브러리 중에 지리 공간 데이터를 처리하는데 사용하는 Turf.js라는 라이브러리가 있었다. 이 라이브러리에 <code>convex</code>라는 메서드가 있는데, <strong>2차원 좌표 평면에 존재하는 점들의 집합을 감싸는 최소 크기의 볼록 다각형을 만들어준다.</strong> 이를 이용하면 그림자를 쉽게 만들 수 있지 않을까 생각했다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/86fa14a9-34bf-4892-8a90-9139d36d5d1b/image.png" width="50%"/>
<p/>


<p>아까 그림자 길이를 계산했던 것을 토대로, 건물의 윗면이 바닥에 정사영되는 위치를 알 수 있고, 모든 건물은 기둥 형태라는 가정을 통해 간단하게 <strong>밑면을 그림자 길이 만큼 평행이동</strong> 시킴으로써 <strong>윗면이 정사영 되는 위치를 쉽게 계산할 수 있다.</strong> 그리고 <strong>기존 밑면을 이루던 점들과 윗면이 정사영 된 점들을 가지고 <code>convex</code> 메서드를 이용하면 그림자와 유사한 형태의 볼록다각형</strong>을 얻을 수 있다.</p>
<h3 id="방법1-구현-결과">[방법1] 구현 결과</h3>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/3760f959-9e6f-4c00-b496-f782f61cf702/image.PNG" width="80%"/>
<p/>

<p>위 방법을 통해 구현한 첫 번째 그림자 지도이다. 일단 <strong>대부분의 건물이 기둥 형태</strong>이기도 하고, <strong>대부분의 건물 단면이 볼록다각형 형태</strong>이기 때문에 사용에는 큰 문제가 없어보였다. 다만, 지도를 넘기다보면 간혹 <strong>오목한 형태의 단면을 가진 건물</strong>들이 있는데, <strong>이 경우에 그림자 모양에 문제</strong>가 생겼다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/d282a5dc-38fb-4b9e-af16-9df5d06c4502/image.PNG" width="60%"/>
<p/>

<p>사실 주사용 목적 자체가 고층 빌딩으로 인해 생기는 그림자를 이용하려고 만든 앱이라 내가 사용하기엔 큰 문제는 없었다. 하지만... 누군가에게 소개했을 때 이런 것들이 하나 보일 때마다 <strong>이 앱의 신뢰도에 문제</strong>가 생길 수 있겠다고 생각했다. 그래서 <strong>조금 복잡해질 수는 있지만, 더욱 정교한 그림자를 그려보기로</strong> 했다.</p>
<h3 id="방법2-건물의-모든-면에-대해-그림자-생성">[방법2] 건물의 모든 면에 대해 그림자 생성</h3>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/4e1e0806-b444-4b72-beef-61e2fe93c356/image.PNG" width="70%"/>
<p/>

<p>사실 이게 그림자를 그리는 정석적인 방법일 것 같다. 원래는 햇빛이 닿는 면에 대해서만 그림자를 그려주면 되지만, 햇빛이 닿는 면인지 계산하는 로직이 훨씬 더 복잡할 것 같아서, 단순하게 <strong>모든 옆면과 윗면에 대해 생기는 그림자를 다 그리도록</strong> 했다.</p>
<p>제공되는 건물 3D 데이터에는 건물 밑면의 폴리곤 데이터와 높이만 있었기 때문에 건물 밑면에서 <strong>연속한 두 개의 점과 높이를 통해 한 면의 그림자</strong>를 생성했다.</p>
<h3 id="방법2-구현-결과">[방법2] 구현 결과</h3>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/26b1862a-317d-410d-95dd-4edec2b24999/image.PNG" width="80%"/>
<p/>

<p>이로써 <strong>그림자가 더욱 정교</strong>해졌다. 다만 조금 아쉬운건 여러 그림자를 Turf.js의 <code>union</code> 메서드를 이용해서 하나의 그림자로 합치고 싶었는데, 이 부분에서 계속 에러가 발생해 일단 보류중이다. 그래서 일단 각 면마다 각각의 그림자가 그려지고, 많이 겹쳐진 부분은 좀 더 어두운 색을 띄고, 그렇지 않은 부분은 연한 색을 띈다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/94f39622-472e-4834-a04d-c1e592b35200/image.PNG" width="50%"/>
<p/>

<p>기존에 이상하게 그려졌던 오목한 형태의 건물도 꽤나 그림자다워진 모습을 볼 수 있다.</p>
<ul>
<li><strong>변경 전 INP</strong></li>
</ul>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/4f05c592-e39d-4b5a-9647-fba3dbd3272d/image.PNG" width="60%"/>
<p/>

<ul>
<li><strong>변경 후 INP</strong></li>
</ul>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/a78b52d6-78bf-4513-978a-1553cbda3c53/image.PNG" width="60%"/>
<p/>

<p>비록 변경 후에 <strong>INP 속도가 두 배 이상으로 느려지긴 했지만...</strong> 기존 속도가 워낙에 빨랐고, 지금도 크게 느리게 느껴지지는 않아서 <strong>성능을 포기하고 미학을 선택</strong>했다. 하지만, 화면을 빠르게 이동했을 때 변경 전에 비해 끊기는 느낌이 생긴 것은 사실이다. 이런 부분은 <strong>디바운스</strong> 처리를 하거나 로드한 건물 3D 레이어나 그림자 레이어를 <strong>캐싱</strong>할 수 있는지 등등 <strong>다른 방법으로 최적화를 할 수 있지 않을까 생각</strong>한다. 그리고 Turf.js의 <code>union</code> 메서드 에러를 해결하면 조금은 나아지지 않을까 싶다.</p>
<ul>
<li>** (✅추가 작업) 디바운스 설정 이후 INP**</li>
</ul>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/c5347fca-47c4-412d-8390-7f8135b3689e/image.PNG" width="60%"/>
<p/>

<p>이후에 추가적으로 <strong>디바운스 유틸 함수</strong>를 만들어서, 위도, 경도, 줌, 각도 값들이 변할 때마다 계속 요청을 보내는 것이 아닌, 100ms 간격 이내의 연속적인 요청에 대해서는 기존 요청을 클리어 후 새로운 요청을 보내도록 설정했다. <strong>디바운스 설정 후에 성능이 비약적으로 상승</strong>한 것을 확인할 수 있었다. 그런데, <strong>INP 지표가 의미하는 것이 인터렉션 이후 처음으로 무언가 다시 그려지기까지의 시간</strong>을 측정하는 것 같은데, <strong>그림자는 분명 늦게 로드되고 있지만 3D 건물 레이어가 로드되는 시점을 기준으로 측정되어 빠르게 나온 것 같다는 느낌</strong>이 들었다. 이 부분에 대해서는 추가적으로 찾아봐야 할 것 같다.</p>
<h1 id="사용-후기">사용 후기</h1>
<p>요즘 부트캠프 오프라인 출석차 강남으로 가는데, 솔직히 특정 목적지를 가기 위해 이 앱을 사용하지는 않는다. 아무리 덥다고 한들 1초라도 빨리 목적지에 도착하는 게 좋기 때문에 무조건 최단거리로만 간다. 다만, 가기 전에 카페를 들러야 하는 경우 사용하고 있다. 강남역 인근은 어디로 가든 카페 하나쯤은 있기 마련이다. 그래서 맵을 켜서 <strong>그늘 위치를 확인한 후 그늘이 많은 쪽에 있는 카페를 경유하여 목적지를 가고 있다.</strong> </p>
<p>사실 카카오맵이나 네이버지도 등의 너무 친절한 UI를 보다가 이 Mapbox의 투박한 UI를 보고 있으면 어디가 어딘지 헷갈리긴 한다. 그래서 이 앱을 블로그에 소개할까 망설이기도 했다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/bc1654e0-f952-4320-9064-9d310322d366/image.jpg" width="60%"/>
<p/>

<p>하지만, 한 지인 분께서 잠깐 써보시더니 새로운 사용처를 발견해주셨다. 이 얘길 듣고, 뭐 일단 만들어 놓은거고, 열어놓으면 누군가 또 <strong>예상치 못한 사용법을 발견</strong>해 줄 수도 있지 않겠나 싶었다. 그리고 그림자 그리는 로직을 좀 더 최적화 하고 싶은데, <strong>누군가 조언해 줄 수도 있지 않을까 싶어</strong> 블로그에 소개하게 됐다.</p>
<p>마지막으로 배포중인 URL 링크를 한 번 더 공유하면서 글을 마치도록 하겠다.</p>
<blockquote>
<p>배포중인 URL: <a href="https://project-g-seven.vercel.app/" target="_blank">https://project-g-seven.vercel.app/</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[MCP, Claude, Figma] AI에게 Figma 작업을 시켜보자!]]></title>
            <link>https://velog.io/@jiiker_/MCP-Claude-Figma-AI%EC%97%90%EA%B2%8C-Figma-%EC%9E%91%EC%97%85%EC%9D%84-%EC%8B%9C%EC%BC%9C%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@jiiker_/MCP-Claude-Figma-AI%EC%97%90%EA%B2%8C-Figma-%EC%9E%91%EC%97%85%EC%9D%84-%EC%8B%9C%EC%BC%9C%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Sun, 22 Jun 2025 03:10:23 GMT</pubDate>
            <description><![CDATA[<h1 id="mcpmodel-context-protocol란">MCP(Model Context Protocol)란?</h1>
<p><a href="https://modelcontextprotocol.io/introduction" target="_blank" rel="noopener noreferrer">MCP에 대한 공식 문서</a>를 찾아보면, </p>
<blockquote>
<p>MCP는 애플리케이션이 LLM(대형 언어 모델)에 컨텍스트를 제공하는 방식을 표준화한 오픈 프로토콜입니다. MCP는 마치 USB-C 포트처럼 작동합니다. USB-C가 다양한 기기와 액세서리를 연결하기 위한 표준 포트라면, MCP는 다양한 데이터 소스와 도구를 AI 모델에 연결하는 표준화된 방식을 제공합니다.</p>
</blockquote>
<p>라고 설명이 나와있다. 우리가 사용하는 여러가지 <strong>애플리케이션(Figma, Notion, Slack 등)이 LLM(ChatGPT, Claude, Gemini 등)에 컨텍스트를 제공하는 방식을 표준화한 것</strong>이라고 한다. 이는 쉽게 말해서 <strong>AI가 Figma, Notion과 같은 각각의 애플리케이션이 동작하는 방식을 이해하고, 이를 직접 다룰 수 있게 됨을 의미</strong>한다. 각각의 애플리케이션이 LLM과 직접 소통할 수 있는 소통창구를 열어준 셈이다.</p>
<p>이전까지는 적어도 인간이 AI에게 물어보고 AI가 알려준 정답을 통해 Figma, Notion 등에 옮겼었다면, <strong>이제는 그 노력조차 하지 않아도 된다</strong>는 뜻이다. 마침 새롭게 진행하는 사이드 프로젝트에서 피그마 디자인을 직접 해야했기에 그 과정이 너무 귀찮아 피그마를 연결하는 방법부터 찾아보았다.</p>
<h1 id="figma-mcp-연결-방법feat-talk-to-figma">Figma MCP 연결 방법(feat. Talk To Figma)</h1>
<p>우선은 <a href="https://modelcontextprotocol.io/quickstart/user" target="_blank" rel="noopener noreferrer">MCP 공식문서 빠른 시작 가이드</a>를 살펴보면 아주 간단하게 사용 방법을 소개하고 있다. 이를 따라서 진행해 보자. </p>
<h2 id="claude-설치">Claude 설치</h2>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/d7d5b6d7-003c-4937-809a-a4247af4fdb7/image.PNG" width="50%" />
</p>

<p>우선 <a href="https://claude.ai/download" target="_blank" rel="noopener noreferrer">클로드 공식 홈페이지</a>에 들어가서 운영체제에 맞는 <strong>Claud를 설치</strong>해주자. 클로드 내부의 설정 파일을 직접 수정해주어야 하기 때문에 따로 설치를 진행해야 한다.</p>
<h2 id="bun-설치">Bun 설치</h2>
<p>사실 <a href="https://modelcontextprotocol.io/quickstart/user" target="_blank" rel="noopener noreferrer">MCP 공식문서 가이드</a>에 bun 설치에 관한 안내가 없어서 설치를 안해도 되는 줄 알았다. 하지만, 중간중간 참고하는 사이트마다 bun을 설치하라고 해서 이게 뭔가 찾아봤다.</p>
<blockquote>
<ul>
<li>Bun은 Node.js와 비슷한 자바스크립트 실행 환경(런타임)</li>
<li>많은 MCP 서버들이 Bun 환경에서 실행되도록 만들어졌음</li>
</ul>
</blockquote>
<p>특히, <strong>내가 지금 사용하려고 하는 Talk To Figma MCP 서버의 경우</strong> <code>bunx cursor-talk-to-figma-socket</code> 명령어를 통해 실행하기 때문에 <strong>bun의 설치가 필수</strong>이다.</p>
<h3 id="window">Window</h3>
<p>윈도우의 경우 cmd를 열어</p>
<pre><code class="language-bash">powershell -c &quot;irm bun.sh/install.ps1|iex&quot;</code></pre>
<p>위와 같은 명령어로 설치할 수 있고,</p>
<h3 id="ios">iOS</h3>
<p>맥북(iOS)의 경우에는</p>
<pre><code class="language-bash">curl -fsSL https://bun.sh/install | bash</code></pre>
<p>와 같은 명령어로 설치할 수 있다.</p>
<h3 id="mcp-서버-실행">mcp 서버 실행</h3>
<p>이후에 윈도우의 cmd나 iOS의 터미널을 열어서</p>
<pre><code class="language-bash">bunx cursor-talk-to-figma-socket</code></pre>
<p><strong>명령어를 통해 직접 실행해야만 Talk To Figma MCP 서버가 실행되고, MCP 연결이 가능</strong>해진다.</p>
<h2 id="claude-설정-파일">Claude 설정 파일</h2>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/ea013279-1a12-48ae-8f3c-fed397ad8382/image.PNG" width="50%" />
</p>

<p>다음으로 윈도우 기준으로는 <strong><code>파일</code> &gt; <code>설정</code></strong>으로 들어가면 되고, 맥북(iOS)의 경우에는 상단 탭의 <strong><code>Claude</code> &gt; <code>Settings...</code></strong>로 들어가보자. 그 중 <strong><code>개발자</code> 탭</strong>을 눌러보면 위와 같은 화면을 확인할 수 있다. 여기서 <strong><code>설정 편집</code></strong>을 눌러보자.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/78dbb89b-7c29-4e6b-9ef1-9bde0a66db2b/image.PNG" width="50%" />
</p>

<p>그럼 위와 같은 폴더가 열리면서 <strong>설정 파일이 포커싱</strong>된다. 해당 파일을 <strong>아무 문서 편집 툴을 이용해서 열어보자.</strong> 나는 VS Code가 제일 익숙했기 때문에 VS Code를 이용해서 열어보았다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/0d39584b-02e9-40fd-a638-d12cd4c67032/image.PNG" width="50%" />
</p>

<p>그럼 위와 같은 JSON 파일이 열리는데, 처음에는 아무것도 설정되어있지 않은 빈 파일이다. <strong>여기에 우리가 필요한 MCP 설정들을 추가</strong>해주면 Claude에서 해당 기능들을 사용할 수 있게 되는 것이다.</p>
<h2 id="claude-설정-json-코드">Claude 설정 JSON 코드</h2>
<h3 id="mcp-github">MCP Github</h3>
<p>우선 <strong>해당 MCP의 Github 레포지토리</strong>에도 사용방법이 자세하게 안내되어 있다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/5d6c36d2-76bf-453d-bfd1-85dd16f60f44/image.PNG" width="60%" />
</p>

<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/e28aced6-c739-4c32-901c-8200c9921779/image.PNG" width="60%" />
</p>

<p>나중에 MCP를 직접 커스텀 해서 사용한다거나 <strong>확장성을 고려했을 때는 Github를 이용하는 게 가장 좋을 것 같다.</strong> 하지만, 이것저것 복잡해보여서 일단 MCP 찍먹을 위해 좀 더 간편한 방법을 찾아보았다.</p>
<h3 id="smithery">Smithery</h3>
<p>그렇게 <strong>더 간편한 방법이 없을까 찾아보다 알게 된 사이트가 <a href="https://smithery.ai/">Smithery</a>였다.</strong> Smithery는 내가 원하는 MCP를 찾을 수 있는 플랫폼이다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/66b07cdc-a749-40be-a69c-3b96ece90481/image.PNG" width="60%" />
</p>

<p>검색을 통해 내가 원하는 MCP를 쉽게 찾을 수 있고,</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/67089e92-cc82-4dfe-8477-182713153b8b/image.PNG" width="60%" />
</p>

<p>여기서 LLM에 제공할 MCP JSON 설정 코드를 볼 수 있다. <strong>MCP 서버 이름이 &quot;cursor-...&quot;로 시작</strong>하지만, <strong>MCP 설정은 클로드나 커서나 동일하기 때문에 클로드에서 이걸 사용해도 상관없다</strong>고 한다.</p>
<h3 id="figma-plugin">Figma Plugin</h3>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/e0991353-f7a2-4de5-bbfb-be9abc8fea49/image.PNG" width="70%" />
</p>

<p>그런데 피그마의 경우 <strong>피그마 사이트에서 제공하는 <a href="https://www.figma.com/community/plugin/1485687494525374295/cursor-talk-to-figma-mcp-plugin" target="_blank" rel="noopener noreferrer">Talk To Figma MCP Plugin</a>이 있었다.</strong> 뭔가 제일 신뢰가 가기도 했고, Smithery에서 가져온 JSON 코드를 입력했을 때 계속해서 에러가 발생해서 이걸 사용했는데, 말끔하게 해결이 되었다. 그렇기 때문에 이번 포스팅에서는 이 방법을 소개하고, Smithery를 이용하는 방법은 추후에 좀 더 살펴보도록 하자.</p>
<p>위의 Plugin 페이지에서 <strong><code>Open in...</code></strong>을 눌러서 <strong>연동을 원하는 피그마 프로젝트를 선택</strong>해서 들어가면,</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/ce3dc6bd-1d1d-4a09-a6ac-a29dc9cc8bab/image.PNG" width="60%" />
</p>

<p>위와 같은 화면이 뜨고, 여기서 <strong>우측 하단의 <code>Run</code>을 눌러서 실행</strong>시켜 주자.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/1a5f76a2-0337-4fbf-bd98-b9b808e3c7f6/image.PNG" width="80%" />
</p>

<p>이 때, 위와 같이 <strong>연결된 서버의 채널</strong>이 뜨고, <strong><code>mcp.json</code> 파일</strong>이 보이면, <strong>성공적으로 연결</strong>된 것이다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/a654c04b-c11a-429b-b638-217f547a7ad6/image.PNG" width="50%" />
</p>

<p>해당 <strong>JSON 코드를 클로드 설정 파일에 붙여 넣은 후</strong>, <strong>클로드를 완전히 종료했다가 다시 실행</strong>시켜주자.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/821914e4-dcc9-46a5-953a-58f5ad5ad4f4/image.PNG" width="100%" />
</p>

<p>이제 클로드에서 <strong>해당 피그마 프로젝트 채널에 연결해서 작업</strong>해달라고 요청하면 <strong>클로드가 자동으로 작업을 수행</strong>해주는 것을 확인할 수 있다. 클로드가 무료 버전이라 조금 복잡한 작업을 맡기면 가능한 대화수를 초과했다고 뜬다.(지금은 빨간색 동그라미에 만족하도록 하자...) 우선은 연동 방법만 익혀두고 추후에 커서나 클로드 유료 버전을 결제해서 마음껏 사용해봐야겠다!!</p>
<h2 id="🛠️-트러블-슈팅">🛠️ 트러블 슈팅</h2>
<h3 id="🚨-bunx-명령어-찾을-수-없음">🚨 bunx 명령어 찾을 수 없음</h3>
<blockquote>
<p>Could not connect to MCP server TalkToFigma
MCP TalkToFigma: spawn bunx ENOENT
MCP TalkToFigma: Server disconnected. For troubleshooting guidance, please visit our debugging documentation</p>
</blockquote>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/12e536c3-cf26-48ba-9627-5a1685e9d29d/image.PNG" width="60%">
</p>

<p>처음에 <strong>bun을 설치하지 않고, Talk To Figma MCP의 JSON 설정 코드를 클로드 설정 파일에 넣었을 때</strong>, 클로드 실행시 만난 에러이다. <strong>bun을 설치</strong>해줌으로써 해결할 수 있다.</p>
<h4 id="🎯-ios-추가-이슈">🎯 iOS 추가 이슈</h4>
<p>그런데 <strong>맥북(iOS)의 경우 bun을 설치한 후에도 위와 같은 에러</strong>가 떴었는데, <a href="https://velog.io/@innes_kwak/MCP-MCP-%EC%97%B0%EA%B2%B0-%ED%8A%B8%EB%9F%AC%EB%B8%94%EC%8A%88%ED%8C%85-TalkToFigma-Claude#%EB%AC%B8%EC%A0%9C-3-claude%EC%97%90%EC%84%9C-bunx-%EB%AA%85%EB%A0%B9%EC%96%B4%EB%A5%BC-%EC%B0%BE%EC%9D%84-%EC%88%98-%EC%97%86%EC%9D%8C-spawn-bunx-enoent-%EC%97%90%EB%9F%AC" target="_blank" rel="noopener noreferrer">다른 벨로그의 트러블 슈팅 글</a>을 보고 해결할 수 있었다.</p>
<p>우선 <strong>path 설정과 관련한 문제</strong>였는데, <code>~/.zshrc</code> 파일에서</p>
<pre><code class="language-bash">export BUN_INSTALL=&quot;$HOME/.bun&quot;
export PATH=&quot;$BUN_INSTALL/bin:$PATH&quot;</code></pre>
<p>와 같이 경로 설정이 되어있는지 확인해보자. 내 경우에는 <strong>경로 설정은 위와 동일하게 되어있었는데, 명령어를 입력하면 명령어를 찾을 수 없다</strong>고 떴었다.</p>
<p>그 때,</p>
<pre><code class="language-bash">source ~/.zshrc</code></pre>
<p><strong>위 명령어를 한 번 실행해주니 정상적으로 명령어가 인식</strong>됐다.</p>
<p>명령어는 정상적으로 동작했지만 <strong>여전히 같은 에러</strong>가 떴는데, 위 블로그에서 해결한 것 처럼</p>
<pre><code>&quot;mcpServers&quot;: {
  &quot;TalkToFigma&quot;: {
    &quot;command&quot;: &quot;/Users/[유저이름]/.bun/bin/bunx&quot;,
    &quot;args&quot;: [&quot;cursor-talk-to-figma-mcp@latest&quot;, &quot;--server=vps.sonnylab.com&quot;]
  }
}</code></pre><p><strong>명령어 경로를 절대 경로를 이용</strong>하여 <code>/Users/Jiiker/.bun/bin/bunx</code>와 같이 지정해서 해결했다. <code>[유저이름]</code><strong>에는 본인의 계정 이름을 입력</strong>하면 된다.</p>
<h3 id="🚨-mcp-서버-미실행">🚨 MCP 서버 미실행</h3>
<blockquote>
<p>Disconnected from server</p>
</blockquote>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/14838ac5-559f-4450-a621-d7ae6ce73cb0/image.PNG" width="80%">
</p>

<p>Talk To Figma Plugin에서 Connect를 눌러도 서버 연결이 끊겼다고 뜬다. 아주 친절하게 바로 아래에 안내가 나와있다. <strong>윈도우의 cmd 혹은 iOS 터미널을 열어 아래의 명령어를 실행</strong>시켜주자.</p>
<pre><code>bunx cursor-talk-to-figma-socket</code></pre><p>이렇게 <strong>직접 실행시켜 줘야지 Talk To Figma MCP 서버가 실행되고, MCP 연결이 가능</strong>해진다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 자바스크립트에서 비동기를 처리하는 방법(feat. Callback, Promise, async/await)]]></title>
            <link>https://velog.io/@jiiker_/React-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%97%90%EC%84%9C-%EB%B9%84%EB%8F%99%EA%B8%B0%EB%A5%BC-%EC%B2%98%EB%A6%AC%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95feat.-Callback-Promise-asyncawait</link>
            <guid>https://velog.io/@jiiker_/React-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%97%90%EC%84%9C-%EB%B9%84%EB%8F%99%EA%B8%B0%EB%A5%BC-%EC%B2%98%EB%A6%AC%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95feat.-Callback-Promise-asyncawait</guid>
            <pubDate>Mon, 09 Jun 2025 10:21:19 GMT</pubDate>
            <description><![CDATA[<h1 id="비동기asynchronous란">비동기(Asynchronous)란?</h1>
<h2 id="동기synchronous-vs-비동기asynchronous">동기(Synchronous) vs 비동기(Asynchronous)</h2>
<ul>
<li><strong>동기</strong>: 작업이 순차적으로 실행되어 <strong>이전 작업이 끝나야 다음 작업이 실행되는 구조</strong>를 말한다.</li>
<li><strong>비동기</strong>: <strong>이전 작업의 완료를 기다리지 않고 다음 작업이 실행</strong>되는 구조를 말한다.</li>
</ul>
<p>예를 들어, 요리를 하면서 물을 끓이는 작업을 동기로 처리하면 물이 끓을 때까지 아무 일도 하지 못하지만, 비동기로 처리하면 물을 끓이는 동안 재료를 손질할 수 있는 것과 같다.</p>
<h2 id="블로킹blocking-vs-논블로킹non-blocking">블로킹(Blocking) vs 논블로킹(Non-blocking)</h2>
<ul>
<li><strong>블로킹</strong>: 하나의 작업이 끝날 때까지 <strong>다른 작업이 멈추는 구조</strong>를 말한다.  </li>
<li><strong>논블로킹</strong>: 작업을 요청한 뒤 <strong>기다리지 않고 다음 작업을 계속 수행하는 구조</strong>를 의미한다.</li>
</ul>
<p>여기서 처음에 동기와 비동기를 구분하는 기준과 블로킹과 논블로킹을 구분하는 기준에 무슨 차이가 있는지 애매모호했다. 내가 이를 정리한 방법은 <strong>지체되지 않고 다음 작업이 실행 되는지 여부</strong>를 중점적으로 따지는 것이 <strong>동기와 비동기의 구분의 핵심</strong>인 느낌이었고, <strong>기존 작업의 제어권을 넘김으로써 기존 작업이 멈추는지 여부</strong>를 중점적으로 따지는 것이 <strong>블로킹과 논블로킹을 구분하는 핵심</strong>이라 느꼈다.</p>
<p>이처럼 동기와 비동기, 블로킹과 논블로킹은 별도의 개념이고, 결과적으로 함수의 실행 방식에는 총 4가지의 방식이 존재한다.</p>
<ul>
<li>동기 + 블로킹</li>
<li>동기 + 논블로킹</li>
<li>비동기 + 블로킹</li>
<li>비동기 + 논블로킹</li>
</ul>
<p>여기서 우리가 익숙한 구조는 &#39;동기 + 블로킹&#39;, &#39;비동기 + 논블로킹&#39;이고, <strong>일반적으로 동기 작업은 &#39;동기 + 블로킹&#39;, 비동기 작업은 &#39;비동기 + 논블로킹&#39; 구조로 구현되는 경우가 많기 때문</strong>에, 이렇게 이해해도 큰 무리는 없다. 4가지 방식의 차이점에 대해서는 추후 새로운 포스팅에서 더 자세히 알아보도록 하자.</p>
<h2 id="자바스크립트에서-비동기의-필요성">자바스크립트에서 비동기의 필요성</h2>
<p>자바스크립트는 브라우저에서 실행되는 대부분의 코드(이벤트 처리, API 호출, 파일 읽기 등)를 처리하기 위해 <strong>비동기 방식</strong>을 채택하고 있다. 자바스크립트는 <strong>싱글 스레드 언어</strong>이기 때문에, 만약 모든 작업을 동기적으로 처리한다면 <strong>사용자는 웹페이지가 멈춘 것처럼 느낄 수 있다.</strong></p>
<hr>
<h1 id="자바스크립트의-비동기-처리-방식">자바스크립트의 비동기 처리 방식</h1>
<h2 id="콜백callback-함수">콜백(Callback) 함수</h2>
<p>자바스크립트 초창기에는 비동기 작업이 끝난 후 실행될 작업을 관리할 수단이 콜백함수 밖에 없었기 때문에, <strong>콜백함수를 인자로 전달</strong>하는 방식이 주로 사용되었다.</p>
<pre><code class="language-js">function getData(callback) {
  setTimeout(() =&gt; {
    callback(&#39;데이터 도착&#39;);
  }, 1000);
}

getData(function (result) {
  console.log(result); // 1초 후 &#39;데이터 도착&#39; 출력
});</code></pre>
<ul>
<li>장점: 간단하다.  </li>
<li>단점: 중첩이 깊어지면 콜백 지옥(callback hell)이 발생하여 유지보수가 어렵다.</li>
</ul>
<h3 id="💀-콜백-지옥callback-hell이란">💀 콜백 지옥(Callback Hell)이란?</h3>
<p>예를들어 <strong>특정 유저의 특정 게시글에 달린 댓글들을 조회하는 상황</strong>이라고 가정했을 때 <strong>유저를 조회하는 비동기 작업에 해당 유저의 게시글을 조회하는 함수를 콜백 함수로 전달</strong>해야 하고, <strong>해당 함수에 또 다시 해당 게시글에 달린 댓글을 조회하는 함수를 콜백 함수로 전달</strong>해야 한다. 이런 식으로 콜백 함수를 중첩해서 사용하는 구조를 <strong>콜백 지옥(callback hell)</strong>이라고 부른다. 콜백 함수를 중첩해서 사용하게 되면 코드에 들여쓰기가 계단처럼 들어가는 구조를 띄게 되기 때문에 중첩 횟수가 늘어남에 따라 가독성이 매우 떨어지게 된다.</p>
<pre><code class="language-javascript">getUser(userId, (user) =&gt; {
  getPostsByUser(user.id, (posts) =&gt; {
    getCommentsByPost(posts[0].id, (comments) =&gt; {
      console.log(&#39;댓글 목록:&#39;, comments);
    });
  });
});</code></pre>
<p>ES6 문법 이후에 Promise가 등장하면서 비동기 작업의 결과에 따라 then이나 catch 체이닝을 통해 콜백 함수를 처리할 수 있게 되면서 콜백 지옥을 해결할 수 있게 되었다.</p>
<h2 id="promise">Promise</h2>
<p>ES6부터는 <strong>Promise 객체</strong>를 통해 <strong>비동기 작업의 성공(resolve) 또는 실패(reject)를 표현할 수 있게</strong> 되었다. 이로써 작업의 결과에 따른 동작을 <strong>체이닝을 통해 좀 더 직관적인 형태로 관리</strong>가 가능해졌다.</p>
<pre><code class="language-js">function getData() {
  return new Promise((resolve, reject) =&gt; {
    setTimeout(() =&gt; {
      resolve(&#39;데이터 도착&#39;);
    }, 1000);
  });
}

getData()
  .then((result) =&gt; console.log(result)) // 1초 후 &#39;데이터 도착&#39;
  .catch((error) =&gt; console.error(error));</code></pre>
<ul>
<li>장점: 체이닝이 가능하고, 에러 처리가 용이하다.  </li>
<li>단점: <code>.then().then()</code>과 같은 체이닝이 길어지면 가독성이 떨어질 수 있다.</li>
</ul>
<h2 id="async--await">async / await</h2>
<p>ES2017(ES8)에서는 <strong>async/await 문법이 도입</strong>되어 <strong>Promise를 더 직관적으로 사용할 수 있게</strong> 되었다.</p>
<pre><code class="language-js">function getData() {
  return new Promise((resolve) =&gt; {
    setTimeout(() =&gt; {
      resolve(&#39;데이터 도착&#39;);
    }, 1000);
  });
}

async function showData() {
  const result = await getData();
  console.log(result); // 1초 후 &#39;데이터 도착&#39;
}

showData();</code></pre>
<ul>
<li>장점: 동기 코드처럼 깔끔하게 작성할 수 있어 가독성이 매우 좋다.  </li>
<li>단점: 병렬 처리가 필요한 경우에는 주의가 필요하다.</li>
</ul>
<h3 id="❓-await은-blocking-방식일까">❓ await은 blocking 방식일까?</h3>
<p><code>await</code>을 사용하게 되면 해당 <strong>Promise 객체를 반환할 때까지 이후의 작업이 멈추게 된다.</strong> 이 부분에서 <code>await</code>을 blocking라고 생각할 수도 있지만, <code>await</code><strong>은 non-blocking</strong>이다. 함수 흐름에서는 일시 정지처럼 보이지만, <strong>전체 실행 환경(이벤트 루프)은 멈추지 않는다.</strong></p>
<pre><code class="language-js">async function foo() {
  console.log(&#39;1&#39;);
  await new Promise((res) =&gt; setTimeout(res, 1000));
  console.log(&#39;2&#39;);
}

foo();
console.log(&#39;3&#39;);</code></pre>
<h4 id="출력-결과">출력 결과:</h4>
<pre><code>1
3
(1초 뒤)
2</code></pre><ul>
<li><code>1</code>은 <code>foo()</code> 함수 내의 동기 코드이므로 즉시 실행된다.  </li>
<li><code>await</code>는 Promise가 완료될 때까지 함수 흐름을 일시 정지시킨다.  </li>
<li>그 사이 <code>console.log(&#39;3&#39;)</code>은 먼저 실행된다.  </li>
<li>1초 후 <code>console.log(&#39;2&#39;)</code>가 실행된다.</li>
</ul>
<h3 id="🔍-await-이후-코드가-어떻게-다시-실행되는가">🔍 await 이후 코드가 어떻게 다시 실행되는가?</h3>
<p><strong><code>foo()</code> 함수는 <code>await</code>을 만나면 콜스택에서 빠지는데, 그렇다면 <code>await</code> 이후의 코드는 언제, 어떻게 다시 실행되는 것일까?</strong></p>
<p>이 과정을 아래와 같이 정리할 수 있다.</p>
<ol>
<li><code>foo()</code>가 호출되면 콜스택에 올라가고, 함수 내부의 동기 코드인 <code>console.log(&#39;1&#39;)</code>이 실행되어 <code>1</code>이 출력된다.</li>
<li>이후 <code>await new Promise(...)</code>를 만나면, 해당 <code>Promise</code>가 처리(resolve)될 때까지 <code>foo()</code> 함수의 실행은 일시적으로 중단된다.</li>
<li>이때 <code>await</code> 이후 코드(<code>console.log(&#39;2&#39;)</code>)는 자동으로 <strong><code>.then()</code> 콜백 형태로 래핑되어 마이크로태스크 큐에 등록될 준비 상태</strong>가 된다.</li>
<li>그런 다음, <code>foo()</code> 함수는 실행을 멈추고 콜스택에서 제거된다.</li>
<li>자바스크립트 엔진은 아직 메인 스크립트의 실행이 끝나지 않았기 때문에, 다음 줄인 <code>console.log(&#39;3&#39;)</code>이 콜스택에 올라가고 실행되어 <code>3</code>이 출력된다.</li>
<li>약 1초 뒤, 브라우저의 타이머가 <code>setTimeout</code> 콜백을 실행하고, 이때 <code>Promise</code>가 resolve되면서 <code>console.log(&#39;2&#39;)</code>가 마이크로태스크 큐에 등록된다.</li>
<li>자바스크립트 이벤트 루프는 콜스택이 완전히 비어 있는 것을 확인한 후, 마이크로태스크 큐에서 <code>console.log(&#39;2&#39;)</code>를 꺼내 콜스택에 push하여 실행한다.</li>
</ol>
<p>결국 <code>await</code>은 <strong>비동기 함수 내부에서 흐름을 잠시 멈출 뿐</strong>, 전체 자바스크립트 실행 환경이나 이벤트 루프 자체는 계속 작동하고 있다. 이러한 구조 덕분에 <code>await</code> 이후 코드가 <strong>나중에, 정확한 타이밍에 맞춰 실행</strong>될 수 있는 것이다.</p>
<hr>
<h1 id="✅-정리">✅ 정리</h1>
<table>
<thead>
<tr>
<th>방식</th>
<th>도입 시기</th>
<th>장점</th>
<th>단점</th>
</tr>
</thead>
<tbody><tr>
<td>Callback</td>
<td>초기</td>
<td>간단하고 빠르다</td>
<td>콜백 지옥, 유지보수 어려움</td>
</tr>
<tr>
<td>Promise</td>
<td>ES6</td>
<td>체이닝 가능, 에러 처리 용이</td>
<td>복잡한 체이닝은 가독성이 떨어진다</td>
</tr>
<tr>
<td>async/await</td>
<td>ES2017</td>
<td>직관적이고 가독성이 좋다</td>
<td>병렬 처리 시 성능 저하 우려</td>
</tr>
</tbody></table>
<p>이처럼 자바스크립트의 비동기 처리 방식은 점차 가독성과 유지보수성을 높이는 방향으로 발전해 왔다. 현대 개발에서는 <strong>async/await 방식이 가장 권장</strong>되는 방식이지만, 콜백과 프로미스 또한 여전히 많이 사용되고 있다. 특히 <strong>async/await은 Promise를 기반으로 한 문법적 설탕(syntactic sugar)</strong>이기 때문에, 실제 개발에서는 콜백, Promise, async/await이 함께 쓰이는 경우가 많다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 최솟값 만들기 - 그리디, 접근 방법, 간단한 증명]]></title>
            <link>https://velog.io/@jiiker_/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%B5%9C%EC%86%9F%EA%B0%92-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EA%B7%B8%EB%A6%AC%EB%94%94-%EC%A0%91%EA%B7%BC-%EB%B0%A9%EB%B2%95-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%A6%9D%EB%AA%85</link>
            <guid>https://velog.io/@jiiker_/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%B5%9C%EC%86%9F%EA%B0%92-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EA%B7%B8%EB%A6%AC%EB%94%94-%EC%A0%91%EA%B7%BC-%EB%B0%A9%EB%B2%95-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%A6%9D%EB%AA%85</guid>
            <pubDate>Thu, 22 May 2025 15:45:55 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-설명">문제 설명</h2>
<p>길이가 같은 배열 A, B 두개가 있습니다. 각 배열은 자연수로 이루어져 있습니다.
배열 A, B에서 각각 한 개의 숫자를 뽑아 두 수를 곱합니다. 이러한 과정을 배열의 길이만큼 반복하며, 두 수를 곱한 값을 누적하여 더합니다. 이때 최종적으로 누적된 값이 최소가 되도록 만드는 것이 목표입니다. (단, 각 배열에서 k번째 숫자를 뽑았다면 다음에 k번째 숫자는 다시 뽑을 수 없습니다.)</p>
<p>예를 들어 A = [1, 4, 2] , B = [5, 4, 4] 라면</p>
<ul>
<li>A에서 첫번째 숫자인 1, B에서 첫번째 숫자인 5를 뽑아 곱하여 더합니다. (누적된 값 : 0 + 5(1x5) = 5)</li>
<li>A에서 두번째 숫자인 4, B에서 세번째 숫자인 4를 뽑아 곱하여 더합니다. (누적된 값 : 5 + 16(4x4) = 21)</li>
<li>A에서 세번째 숫자인 2, B에서 두번째 숫자인 4를 뽑아 곱하여 더합니다. (누적된 값 : 21 + 8(2x4) = 29)</li>
</ul>
<p>즉, 이 경우가 최소가 되므로 29를 return 합니다.</p>
<p>배열 A, B가 주어질 때 최종적으로 누적된 최솟값을 return 하는 solution 함수를 완성해 주세요.</p>
<h3 id="제한-사항">제한 사항</h3>
<ul>
<li>배열 A, B의 크기 : 1,000 이하의 자연수</li>
<li>배열 A, B의 원소의 크기 : 1,000 이하의 자연수</li>
</ul>
<h3 id="입출력">입출력</h3>
<table>
<thead>
<tr>
<th>A</th>
<th>B</th>
<th>answer</th>
</tr>
</thead>
<tbody><tr>
<td>[1, 4, 2]</td>
<td>[5, 4, 4]</td>
<td>29</td>
</tr>
<tr>
<td>[1, 2]</td>
<td>[3, 4]</td>
<td>10</td>
</tr>
</tbody></table>
<hr>
<h2 id="문제-이해">문제 이해</h2>
<p>길이가 같은 자연수 배열이 두 개 있을 때, 각각의 배열에서 자연수를 중복되지 않게 하나씩 뽑아 곱한 값을 누적하여 더하는 간단한 문제이다.</p>
<p>굉장히 흔한 문제이고, 문제 풀이도 잘 알려진 문제이지만 막상 문제를 아무거나 뽑아서 풀다 보니까 처음에 그리디인지 확신을 못 하기도 했고, 그리디로 푸는 과정에서 <strong>&#39;이렇게 정리해두면 계속해서 써먹을 수 있겠다&#39;</strong>는 생각이 들어서 정리해놓고자 벨로그를 켰다.</p>
<hr>
<h2 id="접근-방법">접근 방법</h2>
<p>처음에 아무 생각 없이 풀다가 <strong>누적합 조합을 다 찾으면 되나 싶은 생각에 백트래킹으로 접근</strong>을 했었다.</p>
<h3 id="백트래킹">백트래킹</h3>
<p>백트래킹으로 풀게 되면 최대 1000개의 길이를 가진 배열이기 때문에 A배열 1000개가 각각 B배열 1000개의 선택지를 가지기 때문에 모든 경우를 확인하기 위해서는 <strong>1000의 1000제곱이라는 어마무시한 연산횟수</strong>를 가지게 된다.</p>
<p>보통 <strong>백트래킹에서 실패하는 경우 DP나 그리디를 통해 접근</strong>해 보는데, 여기서는 이전의 연산 결과를 활용할 여지가 없어보여서 DP는 탈락이다.</p>
<h3 id="그리디">그리디</h3>
<p>그제서야 한 가지 아이디어가 떠올랐다. 이걸 쉽게 납득시킬 방법이 없을까 여러 가지로 고민을 해봤는데, 개인적으로 가장 쉬운 방법은 <strong>A배열과 B배열에 의미를 부여</strong>하는 방법이었다.</p>
<p><strong>곱셈</strong>이라는 것은 다르게 말하면 <strong>해당 횟수만큼 반복해서 더하는 것</strong>으로 볼 수 있다. 이 때 <strong>A배열에서 뽑은 숫자</strong>를 <strong>더할 숫자</strong>라고 보고, <strong>B배열에서 뽑은 숫자</strong>를 <strong>반복해서 더할 횟수</strong>라고 생각해보자.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/3cf80553-c907-4b9d-b8ae-3df8c6d2799e/image.PNG" width="80%" />
</p>

<p>이렇게 보면 좀 더 명확하게 보이는 것 같다. <strong>전체 누적합을 최소로 만들고 싶을 때</strong>, A에서 최솟값을 뽑았다면, 얘를 많이 더하고 싶을까? 조금 더하고 싶을까? <strong>최소로 만들기 위해서는 최솟값을 최대한 많이 더해야 한다.</strong></p>
<p>그렇기 때문에 결과적으로 <strong>매순간 A배열과 B배열에서 각각 최솟값과 최댓값을 뽑아서 곱해주면 되는 것</strong>이다. 따라서 <strong>A배열과 B배열을 반대로 정렬</strong>해두고(A가 내림차순이라면 B는 오름차순), <strong>각각의 인덱스에 해당하는 숫자끼리 곱해서 더해주면 된다.</strong></p>
<h2 id="문제-풀이c">문제 풀이(C++)</h2>
<pre><code class="language-cpp">#include &lt;bits/stdc++.h&gt;

using namespace std;

int solution(vector&lt;int&gt; A, vector&lt;int&gt; B) {
    int answer = 0;

    sort(A.begin(), A.end());
    sort(B.begin(), B.end(), greater&lt;&gt;());

    for (int i = 0; i &lt; A.size(); i++) {
        answer += A[i] * B[i];
    }

    return answer;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React Native] 맥(MacOS)에서 React Native CLI 앱 개발 환경 세팅하기! (feat. XCode, Android Studio)]]></title>
            <link>https://velog.io/@jiiker_/React-Native-%EB%A7%A5MacOS%EC%97%90%EC%84%9C-React-Native-CLI-%EC%95%B1-%EA%B0%9C%EB%B0%9C-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85%ED%95%98%EA%B8%B0-feat.-XCode-Android-Studio</link>
            <guid>https://velog.io/@jiiker_/React-Native-%EB%A7%A5MacOS%EC%97%90%EC%84%9C-React-Native-CLI-%EC%95%B1-%EA%B0%9C%EB%B0%9C-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85%ED%95%98%EA%B8%B0-feat.-XCode-Android-Studio</guid>
            <pubDate>Mon, 19 May 2025 09:56:36 GMT</pubDate>
            <description><![CDATA[<h1 id="expo가-아닌-react-native-cli로-시작한-이유">Expo가 아닌 React Native CLI로 시작한 이유?</h1>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/d1752473-7625-4013-8a7b-aa2738c32642/image.PNG" width="70%">
</p>

<p><a href="https://reactnative.dev/docs/environment-setup" target="_blank" rel="noopener noreferrer"><strong>React Native 공식 문서</strong></a>에서도 <strong>Expo 사용을 권장</strong>한다. Expo는 복잡한 환경 세팅 없이도 간편하게 앱을 개발할 수 있는 프레임워크다. 간단한 앱을 빠르게 만들어보고 싶다면 분명 Expo가 적합하다.</p>
<p>하지만 내가 만들고자 하는 앱에는 <strong>모바일 화면 공유 기능</strong>이 반드시 포함되어야 했다. Expo는 기본적으로 제공하는 네이티브 기능 외에는 확장이 제한적이고, 필요한 라이브러리를 자유롭게 추가하기 어려운 편이다. 그리고 기왕 공부하는 김에 React Native의 <strong>근본</strong>부터 배우고 싶다는 마음도 있어서 <strong>React Native CLI로 시작</strong>했다.</p>
<p>나는 주로 Windows 환경에서 개발을 진행하지만, <strong>iOS 시뮬레이터는 macOS에서만 사용 가능</strong>하기 때문에 책상 구석에 있던 맥북을 조심스럽게 꺼내보았다. 추후에는 Windows 환경에서도 세팅을 해보고 내용을 공유해보도록 하겠다.</p>
<hr>
<h1 id="전체-개요">전체 개요</h1>
<p>React Native CLI 환경 세팅은 <strong>설치해야 할 것이 굉장히 많다</strong>. 공식 문서나 블로그 글들이 잘 정리되어 있어 전체적인 흐름을 따라가기 어렵진 않지만, 실제로 진행하면서 <strong>현재 버전과 맞지 않는 것</strong>이나 <strong>자잘한 오류</strong>들을 자주 마주치게 된다.</p>
<p><strong>기본적으로는</strong> <a href="https://reactnative.dev/docs/environment-setup" target="_blank" rel="noopener noreferrer"><strong>React Native 공식 문서</strong></a><strong>를 참고</strong>하여 따라 진행했지만, <strong>따라하면서 세부적인 설정 방법은 **<a href="https://deku.posstree.com/ko/react-native/install-on-mac/" target="_blank" rel="noopener noreferrer"><strong>이 글</strong></a></strong>을 참고**하여 해결했다. 그렇게 하면서도 자잘한 에러를 생각보다 많이 마주쳤던 것 같다. 이 글에서는 그런 부분들을 함께 정리해 보고자 한다.</p>
<h2 id="homebrew-설치">Homebrew 설치</h2>
<p><strong>macOS에서 패키지 설치와 관리를 도와주는 패키지 관리자</strong>이다. <strong>대부분의 의존성 도구들을 Homebrew를 통해 설치</strong>하게 된다. <a href="https://brew.sh/" target="_blank" rel="noopener noreferrer">Homebrew 공식 웹사이트</a>에 나와있는 설치 스크립트로 간단하게 설치할 수 있다.</p>
<pre><code class="language-bash">/bin/bash -c &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)&quot;</code></pre>
<h2 id="rbenv-설치">rbenv 설치</h2>
<p>macOS에는 기본적으로 Ruby가 설치되어 있지만, <strong>iOS 개발에 필요한 Cocoapods가 Ruby 2.7.0 이상의 버전을 요구</strong>하고 있기 때문에, rbenv를 설치하고 <strong>버전 업데이트가 필요</strong>하다. 이 때 <strong>버전이 맞지 않으면 Cocoapods 설치시에 문제가 될 수 있다.</strong></p>
<pre><code class="language-bash">brew install rbenv                 #rbenv 설치</code></pre>
<p>설치 이후에 버전 업데이트를 위해 설치 가능한 버전을 확인 해보자.</p>
<pre><code class="language-bash">rbenv install -l</code></pre>
<p>내 경우에는 아래와 같은 버전들이 확인되었다.</p>
<ul>
<li>3.1.7</li>
<li>3.2.8</li>
<li>3.3.8</li>
<li>3.4.4</li>
<li>jruby-10.0.0.1</li>
<li>mruby-3.4.0</li>
<li>picoruby-3.0.0</li>
<li>truffleruby-24.2.1</li>
<li>truffleruby+graalvm-24.2.1</li>
</ul>
<p>이 중에 3.1.7이 제일 만만해 보여서 이걸로 골랐다.</p>
<pre><code class="language-bash">rbenv install 3.1.7
rbenv global 3.1.7</code></pre>
<p>업데이트까지 마쳤으면 <strong>.zshrc 파일을 열어서 아래의 환경변수 구문을 추가</strong>해주자.</p>
<pre><code class="language-bash">vi ~/.zshrc</code></pre>
<p>맨 아래에 다음과 같은 <strong>구문을 추가</strong>한 뒤 <strong>저장하고 닫아주자.(<code>:wq</code>)</strong></p>
<pre><code>eval &quot;$(rbenv init - zsh)&quot;</code></pre><p>이후 <strong>환경 변수를 적용</strong>시켜주자.</p>
<pre><code class="language-bash">source ~/.zshrc</code></pre>
<h2 id="nodejs-설치">Node.js 설치</h2>
<p>React Native는 JavaScript(TypeScript)로 앱을 개발하는 프레임워크이기 때문에 브라우저가 아닌 환경에서 <strong>JavaScript를 실행할 수 있게 해주는 런타임인 Node.js가 반드시 필요</strong>하다.</p>
<p><a href="https://nodejs.org/ko" target="_blank" rel="noopener noreferrer">Node.js 공식 웹사이트</a>에서 직접 다운로드 받을 수도 있고, Homebrew 명령어를 통해 간단하게 설치할 수도 있다.</p>
<pre><code class="language-bash">brew install node                  #Node.js 설치</code></pre>
<p>Node.js를 설치하면 <strong>Node.js 패키지 매니저인 npm도 같이 설치</strong>된다. 설치가 잘 되었는지 확인하기 위해서는 아래와 같은 명령어를 통해 버전 확인을 할 수 있다.</p>
<pre><code class="language-bash">node -v                            #Node.js 버전 확인
npm -v                               #npm 버전 확인</code></pre>
<h2 id="watchman-설치">Watchman 설치</h2>
<p>개발 중 코드가 바뀌면 <strong>파일 변경을 감지해서 자동으로 변경 사항을 반영</strong>해주는 도구이다. 이런 방식을 <strong>Fast Refresh</strong>라고 하는데, 파일을 변경 후 저장했을 때, <strong>변경된 파일만 다시 로드하고 필요한 부분만 리렌더링해서 앱을 빠르게 업데이트 해주는 기능</strong>이다.</p>
<p>필수는 아니지만 개발 효율을 떨어뜨릴 수 있는 여러 문제가 생길 수 있기에 <strong>설치가 강력하게 권장</strong>된다.</p>
<pre><code class="language-bash">brew install watchman               #watchman 설치</code></pre>
<h2 id="🙅♂️-react-native-cli-설치-❌">🙅‍♂️ React Native CLI 설치 (❌)</h2>
<p><a href="https://deku.posstree.com/ko/react-native/install-on-mac/" target="_blank" rel="noopener noreferrer">내가 참고했던 글</a>에서는 React Native CLI를 설치하라고 되어있는데, 찾아보니 <strong>현재(2024년 12월 31일 이후)는 지원 중단</strong>이 되었다... 따라서 <code>react-native init</code> 명령어 또한 사용할 수 없게 됐다.</p>
<p>하지만 이제는 <strong>별도의 React Native CLI 설치 없이 React Native 프로젝트를 생성할 수 있는 방법이 있기 때문</strong>에 이 부분은 뒤에서 설명하도록 하겠다.</p>
<h2 id="xcode-설치">Xcode 설치</h2>
<p>React Native로 <strong>iOS 앱 개발을 하기 위해서는 iOS 개발 툴인 Xcode가 필요</strong>하다. 이 때문에 Window에서는 Xcode를 설치할 수 없어서 iOS 개발이 쉽지않다. <a href="https://apps.apple.com/us/app/xcode/id497799835?mt=12" target="_blank" rel="noopener noreferrer">Mac App Store</a>에서 Xcode를 설치하도록 하자.</p>
<p>Xcode 설치가 완료되면, <strong>Command Line Tools를 설정</strong>해줘야 한다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/866a26c9-e564-4708-8a2f-78336fe4285a/image.png" width="40%">
</p>

<p>Xcode를 실행하고 상단 메뉴에서 <strong><code>Xcode</code> &gt; <code>Settings...</code> 로 이동</strong>한 뒤</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/e35dd254-1c36-40cf-add2-7c51973bb8bf/image.png" width="80%">
</p>

<p><strong>Locations 탭</strong>에서 <strong>Command Line Tools가 잘 설정되어있는지 확인</strong>해보자. 만약에 설정이 안 되어있다면, 드롭다운을 클릭해서 선택해주거나 아래의 명령어를 통해 설정할 수 있다.</p>
<pre><code class="language-bash">xcode-select --install             #Command Line Tools 설치</code></pre>
<h2 id="cocoapods-설치">Cocoapods 설치</h2>
<p><strong>Cocoapods는 iOS 앱 개발에 사용되는 네이티브 모듈의 의존성 관리를 담당</strong>하는 툴이다. React Native 프로젝트를 만들면 <strong>iOS 관련 네이티브 모듈</strong>들이 자동으로 추가된다. 이런 모듈들은 내부적으로 Swift나 Objective-C로 구현된 <strong>네이티브 코드를 포함</strong>하고 있는데, iOS 빌드시 제대로 동작하게 만드려면 <strong>Cocoapods가 이 모듈들을 Xcode 프로젝트에 연결해줘야 하는 것이다.</strong></p>
<p>아래와 같은 명령어를 통해 설치할 수 있다.</p>
<pre><code class="language-bash">sudo gem install cocoapods         #Cocoapods 설치</code></pre>
<p>나는 <strong>설치시 아래와 같은 에러</strong>를 만났다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/b8da8649-af62-45b8-bc66-f8a420825318/image.png" width="80%">
</p>

<p>이 에러가 위에서 언급했던 <strong>Ruby 버전 이슈</strong>였고, 읽어보면 <strong>2.7.0 이상의 Ruby 버전이 필요</strong>하다고 한다. 이를 해결하기 위해서는 <a href="#rbenv-%EC%84%A4%EC%B9%98">위에 언급한 방법</a>으로 <strong>Ruby 버전을 업데이트 해주면 된다.</strong></p>
<h2 id="jdk-설치-java-17">JDK 설치 (Java 17)</h2>
<p>Android 빌드를 위해 Gradle이 사용되는데, Gradle이 Java 기반으로 만들어진 빌드 도구이기 때문에 Java가 필요하다. <a href="https://deku.posstree.com/ko/react-native/install-on-mac/" target="_blank" rel="noopener noreferrer">내가 참고했던 글</a>에서는 <strong>AdoptOpenJDK8</strong>를 설치하라고 되어있는데, <strong>현재는 공식 명칭이 바뀌어서 Temurin을 통해 설치</strong>할 수 있다. 어떤 걸 설치해야하나 고민하다 <a href="https://reactnative.dev/docs/environment-setup" target="_blank" rel="noopener noreferrer">React Native 공식 문서</a>에 있는 <strong>Zulu OpenJDK를 설치</strong>했다.</p>
<p><strong>React Native는 Java 17까지 안정적으로 지원</strong>한다고 한다.</p>
<pre><code class="language-bash">brew install --cask zulu@17        #Zulu OpenJDK 설치
brew info --cask zulu@17            #버전 확인</code></pre>
<p>이 또한 <strong>버전이 맞지 않을 경우에 안드로이드 빌드시에 에러를 마주할 수 있으니 호환되는 버전을 잘 확인하고 설치</strong>하도록 하자. 필자는 뒤에 17을 빼먹어서 최신 버전을 설치 되었는데, 확인해보니 Java 24가 설치되었고, 결국 호환성 문제로 Java 17로 버전을 다운그레이드 했다.</p>
<p>혹시 필자처럼 <strong>자바 버전을 잘못 설치한 경우</strong>, 지우고 Java 17을 다시 설치해주면 된다.</p>
<pre><code class="language-bash">brew uninstall --cask zulu         # 현재 설치된 최신 Zulu 제거
brew install --cask zulu17         # Zulu JDK 17 설치</code></pre>
<h2 id="android-studio-설치">Android Studio 설치</h2>
<p>React Native로 Android 앱을 개발하기 위해서는 Android Studio가 필요하다. <strong>Android 앱을 빌드하고 실행시켜주는 것 뿐만 아니라 에뮬레이터(가상 디바이스)를 기본적으로 제공</strong>해준다. <a href="https://developer.android.com/studio?hl=ko" target="_blank" rel="noopener noreferrer">Android Studio 공식 웹사이트</a>에서 <strong>스크롤을 쭉 내려보면 여러 패키지 중에 선택해서 설치할 수 있고</strong>, 어떤 걸 설치해야할지 모르겠다면 챗지피티에게 살짝 물어보자.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/522226a3-b8a2-4926-aeca-e3891cd766ee/image.PNG" width="70%">
</p>

<p>내 경우에는 ARM이 적혀있지 않은 <code>android-studio-2024.3.2.14-mac.dmg</code>를 설치하면 된다고 한다.</p>
<p><a href="https://deku.posstree.com/ko/react-native/install-on-mac/" target="_blank" rel="noopener noreferrer">내가 참고했던 글</a>에서는 설치 과정에서 여러 가지 선택 사항이 있었던 것 같은데, 내 경우에는 Standard/Custom 선택을 제외하고는 딱히 선택할 게 없어서 이것만 Custom으로 선택하고 전부 <code>Next</code>를 눌러 빠르게 넘겼던 것 같다.</p>
<h3 id="sdk-설정">SDK 설정</h3>
<p>참고했던 글에서는 오른쪽 하단이라고 되어있었는데, 인터페이스가 달라져서 찾기가 힘들었다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/49fe2e9d-8feb-48d9-b831-ca5b258b06ef/image.png" width="90%">
</p>

<p>내 버전 기준에서는 화면 중앙에 있는 <code>More Actions</code> 드롭다운을 누르면 <strong>SDK Manager</strong>를 찾을 수 있었다. 이를 눌러 들어간 뒤에</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/2c86f5b9-1a57-42b8-934e-b75841f86daa/image.png" width="90%">
</p>

<p>위 화면에서 <code>Show Package Details</code>를 체크하고, 아래 내용을 찾아 선택해주자. 이 부분 또한 <strong>참고했던 글에서 선택하라고 알려준 버전이 조금 오래된(?) 느낌이 들어서 조금 더 최신 버전으로 선택</strong>해주었다.</p>
<ul>
<li>Android SDK Platform 34</li>
<li>Intel x86_64 Atom System Image</li>
<li><del>Google APIs ARM 64 v8a System Image</del>(삭제)</li>
<li>Google APIs Intel x86_64 Atom System Image</li>
</ul>
<p>글을 작성하다 세 번째 항목에 ARM이 적혀있는 걸 보고, 뭔가 느낌이 이상해서 챗지피티에게 물어봤더니 역시나 <strong>Intel 맥에서는 선택하지 않는 것이 좋다는 답변</strong>을 받았다. 이 부분과 관련해서도 <strong>챗지피티 등을 적극 활용해서 본인 개발 환경에 맞게 선택항목을 정하면 좋을 것 같다.</strong></p>
<h3 id="환경-변수-설정">환경 변수 설정</h3>
<p><strong>.zshrc 파일</strong>을 열어서</p>
<pre><code class="language-bash">vi ~/.zshrc</code></pre>
<p><strong>아래 환경변수 구문을 추가</strong>하면 된다.</p>
<pre><code class="language-bash">export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/platform-tools</code></pre>
<p>일반적으로 macOS에서 <strong>Android SDK가 설치되는 위치는 SDK 경로는 <code>~/Library/Android/sdk</code>이기 때문</strong>에 위와 같이 작성해주면 된다. 혹시나 문제가 생길 경우 경로는 <strong>이전의 SDK 설정 화면 상단에 **<code>Android SDK Location</code></strong>에서 확인**할 수 있다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/512bd69f-7da7-4a31-b65d-f612ee4c9277/image.png" width="90%">
</p>

<p>마찬가지로 <strong>환경변수를 적용</strong>시켜주자.</p>
<pre><code class="language-bash">source ~/.zshrc</code></pre>
<hr>
<h1 id="react-native-프로젝트-생성-및-실행">React Native 프로젝트 생성 및 실행</h1>
<p>이제 드디어 환경 세팅을 위해 <strong>필요한 설치가 끝났다</strong>. 이제부터는 React Native 프로젝트를 생성해보자!</p>
<p>위에서 언급했던 것처럼 이제는 <code>react-native-cli</code><strong>를 설치하지 않고도, 프로젝트를 생성</strong>할 수 있게 되었다. <strong>아래의 명령어를 통해 프로젝트를 생성</strong>해주자.</p>
<pre><code class="language-bash">npx @react-native-community/cli init MyApp</code></pre>
<p>해당 프로젝트로 이동하면</p>
<pre><code class="language-bash">cd MyApp</code></pre>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/dd468a06-a869-4ab8-846c-3c652a76e2bf/image.png" width="50%">
</p>

<p>우선 앱 최상단에 있는 <code>App.tsx</code> 파일이 바로 우리가 개발을 시작하게 될 루트 컴포넌트일 것이고, <code>/ios</code>와 <code>/android</code>가 각각의 네이티브 플랫폼용 프로젝트 설정과 코드를 포함하고 있는 디렉토리일 것이다.</p>
<p>일단 무작정 실행시켜 보자.</p>
<h2 id="ios-앱-실행">iOS 앱 실행</h2>
<p>아래의 명령어를 통해 iOS 앱을 빌드하고 실행할 수 있다.</p>
<pre><code class="language-bash">npm run ios</code></pre>
<p>순탄할 것이라고 생각지는 않았지만, 역시나 또 에러를 마주했다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/9bb6171e-ec5b-4f6a-94ee-a0e786ef3330/image.png" width="90%">
</p>

<p>이는 <strong>Cocoapods 설정이 누락</strong>되었을 때 발생하는 에러이다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/b2d520a2-9fce-4a5e-ad8b-b6902e4aadb8/image.png" width="90%">
</p>

<p>처음 프로젝트 생성 후에 Cocoapods를 지금 설치하겠냐는 말에 나는 &#39;전 이미 설치했는데요?&#39; 하며 no를 선택했기 때문인 것이다. <strong>여기서 yes를 눌러서 Cocoapods 설정</strong>을 해주거나 아니면 <strong>프로젝트 루트 디렉토리에 있는 **<code>/ios</code></strong> 디렉토리로 이동해서 <strong><code>pod install</code></strong>을 해줘야 한다.**</p>
<pre><code class="language-bash">cd ios
pod install</code></pre>
<p>이젠 되...ㄱ...
어림도 없지! 바로 에러!</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/e597c769-c2dd-4980-90f4-aeb610f0c837/image.png" width="90%">
</p>

<p>이건 또 무엇인고 하니 <code>/User/dongglim/.../script/react_native_pods.rb</code> 라는 파일을 로드할 수가 없다고 한다. 이 부분을 해결하기 위해 정말 많은 시간이 소요가 됐는데, <strong>결과적으로는 파일을 불러오는 부분의 코드</strong>를 변경했다.</p>
<p><strong>(프로젝트 루트 디렉토리)/ios/Podfile :</strong></p>
<pre><code class="language-ruby"># Resolve react_native_pods.rb with node to allow for hoisting
require Pod::Executable.execute_command(&#39;node&#39;, [&#39;-p&#39;,
  &#39;require.resolve(
    &quot;react-native/scripts/react_native_pods.rb&quot;,
    {paths: [process.argv[1]]},
  )&#39;, __dir__]).strip

platform :ios, min_ios_version_supported
prepare_react_native_project!

linkage = ENV[&#39;USE_FRAMEWORKS&#39;]
if linkage != nil
  Pod::UI.puts &quot;Configuring Pod with #{linkage}ally linked Frameworks&quot;.green
  use_frameworks! :linkage =&gt; linkage.to_sym
end
</code></pre>
<p>여기서 파일을 로드하는 부분을</p>
<pre><code class="language-ruby">require Pod::Executable.execute_command(&#39;node&#39;, [&#39;-p&#39;,
  &#39;require.resolve(
    &quot;react-native/scripts/react_native_pods.rb&quot;,
    {paths: [process.argv[1]]},
  )&#39;, __dir__]).strip</code></pre>
<p><strong>아래 코드와 같이 변경</strong>해주었다.</p>
<pre><code class="language-ruby">require_relative &#39;../node_modules/react-native/scripts/react_native_pods&#39;</code></pre>
<p>이 부분이 뭔가 프로젝트 생성시에 디폴트 설정값을 임의로 변경하는 것 같아서 껄끄럽기도 하고, <strong>추후에 문제가 생길까 싶긴 하다</strong>. 기존의 코드는 <code>require.resolve()</code>를 통해 <code>react_native_pods.rb</code><strong>의 정확한 경로를 찾아서 불러오는 동적인 방식</strong>이다. <strong>이를 좀 더 단순하게 변경</strong>한 것인데, 내가 프로젝트 구조를 크게 변경하지 않는 한 <strong>루트 디렉토리에 node_modules가 있을 것</strong>이고, <strong>그 안에 해당 파일만 온전하게 설치</strong>되어 있으면 잘 동작할 것이기 때문에 이렇게 써줘도 <strong>큰 문제는 없을 것 같아서 이 방식을 채택</strong>했다.</p>
<p>다만, <code>react_native_pods</code>와 관련해서 <strong>설치되는 디렉토리가 변경</strong>된다거나 <strong>프로젝트 구조가 변경됨에 따라 <code>../</code>와 같은 상대경로를 변경해야된다던가</strong> 하는 부분은 스스로 인지하고 있다가 문제가 생겼을 때 잘 대응할 수 있어야 하겠다.</p>
<p>수정한 뒤에 <code>/ios</code> 디렉토리에서 아래 명령어를 실행시켜주면 정상적으로 동작하는 것을 확인할 수 있다.</p>
<pre><code class="language-bash">pod install</code></pre>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/3b60ebc5-4cc0-4b1c-9ac7-522c5b037228/image.png" width="60%">
</p>

<p>뭔진 몰라도 초록색을 보니 마음이 매우 편안하다... 이것 저것 열심히 설치가 진행되고 있구나 싶다...</p>
<p>이후에 다시 처음으로 되돌아가 iOS 앱 실행 명령어를 입력하면</p>
<pre><code class="language-bash">npm run ios</code></pre>
<p>맥북을 너무 오랜만에 꺼내서 그런지 빌드 중에 팬 소리가 꽤나 커진 것 같다... 약 3~4분 가량의 빌드를 마치고 나면 Xcode 시뮬레이터에서 자동으로 앱을 실행시켜준다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/3fbb1ddd-9bb1-4fbf-abbd-3cc1d8436577/image.png" width="30%">
</p>

<p>쫘잔~!! 드디어... 드디어.. 성공했다!</p>
<h3 id="✅-추가-이슈">✅ 추가 이슈</h3>
<p>생각해보니 한 번의 이슈가 더 있었다. 그렇게 큰 이슈는 아니기 때문에 뒤에 짤막하게 추가하도록 하겠다. <code>npm run ios</code>로 실행시켰을 때 만났던 에러인데,</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/e4d5956b-0258-4b46-81d1-709258bdb7eb/image.png" width="80%">
</p>

<p>시뮬레이터가 감지되지 않는다는 에러였다. Xcode에서 시뮬레이터를 설치해줘야하는데, Xcode 설치만 하고 <strong>시뮬레이터를 설치해주지 않아 생긴 에러</strong>였다.</p>
<p>Xcode를 실행하고, 상단 메뉴에서 <code>Xcode</code> &gt; <code>Settings...</code>를 선택하여 설정창으로 들어간다. 그 다음으로 <strong>Components 탭</strong>에 들어가게 되면</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/fb728512-fac4-4eff-a4ee-b845efce9556/image.png" width="70%">
</p>

<p>위와 같은 화면이 뜨는데, 여기서 원하는 기종의 시뮬레이터를 추가할 수 있다. 나는 모바일 앱을 만들거기 때문에 두 번째 줄에 있는 <code>iOS 18.4</code>를 <strong><code>Get</code></strong> 해주었다!!!!</p>
<h2 id="android-앱-실행">Android 앱 실행</h2>
<p>이제 드디어 마지막으로 Android 앱을 실행시켜보자!!</p>
<pre><code class="language-bash">npm run android</code></pre>
<p>다행히 크게 해결이 복잡한 에러는 없었고, 마주쳤던 에러는 하나였다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/43b1eb9f-303e-4627-8a7e-8baec000e2a5/image.png" width="90%">
</p>

<p>이 당시 <strong>설치되어 있던 Java의 버전이 24버전</strong>이었는데, </p>
<pre><code>Unsupported class file major version 68</code></pre><p><code>major version 68</code>이 Java 24버전을 의미하고, &#39;Java 24에서 컴파일된 클래스 파일을 지원하지 않는다.&#39; 뭐 그런 뜻이라고 한다.</p>
<p>이 부분은 <a href="#jdk-%EC%84%A4%EC%B9%98-java-17">위에서 언급했던 것</a> 처럼 지우고 <strong>Java 17을 다시 설치</strong>해주면 된다.</p>
<pre><code class="language-bash">brew uninstall --cask zulu
brew install --cask zulu17</code></pre>
<p>이렇게 해주면, 빌드가 에러 없이 정상적으로 잘 되는 것을 확인할 수 있고</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/jiiker_/post/988bf7a4-744b-466c-800b-f12aaf0ee832/image.png" width="30%">
</p>

<p>아주 깔끔하게 실행이 된다!!!</p>
<hr>
<h1 id="마무리">마무리</h1>
<p><strong>React Native CLI 기반으로 환경을 설정</strong>하는 과정은 <strong>다소 복잡</strong>하지만, <strong>네이티브 기능을 직접 활용할 수 있다는 장점</strong>이 있다. 뿐만 아니라 중간에 발생하는 오류들도 차근차근 해결해가며 학습하면, React Native의 구조와 동작 원리에 대해 더 확실하게 이해할 수 있는 것 같다. 어찌저찌 여러 시행착오들을 겪으면서 앱 개발에 첫 발을 내딛게 되었다.</p>
<p><strong>이젠... 진짜로 앱 개발을 시작해보자!!!</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React Native] Expo와 React Native CLI 무엇을 써야할까?]]></title>
            <link>https://velog.io/@jiiker_/React-Native-Expo%EC%99%80-React-Native-CLI-%EB%AC%B4%EC%97%87%EC%9D%84-%EC%8D%A8%EC%95%BC%ED%95%A0%EA%B9%8C</link>
            <guid>https://velog.io/@jiiker_/React-Native-Expo%EC%99%80-React-Native-CLI-%EB%AC%B4%EC%97%87%EC%9D%84-%EC%8D%A8%EC%95%BC%ED%95%A0%EA%B9%8C</guid>
            <pubDate>Sun, 18 May 2025 04:08:35 GMT</pubDate>
            <description><![CDATA[<h1 id="react-native란">React Native란?</h1>
<p>React Native는 Meta에서 개발한 오픈소스 프레임워크로, JavaScript 또는 TypeScript를 사용하여 <strong>iOS와 Android 앱을 동시에 개발할 수 있는</strong> 기술이다.  </p>
<p>React Native는 React를 기반으로, 브라우저가 아닌 <strong>모바일 플랫폼의 네이티브 컴포넌트(View, Text, Button 등)</strong>를 사용해 UI를 렌더링한다고 한다. 즉, 웹 기술을 기반으로 하면서도 실제 모바일 앱과 같은 성능과 사용자 경험을 제공할 수 있도록 설계된 것이 특징이다.</p>
<p>또한, React Native는 하나의 코드베이스로 여러 플랫폼에서 앱을 개발할 수 있어 <strong>개발 효율성과 유지보수 측면에서 강점을 가진다</strong>고 한다. 필요한 경우, 플랫폼별로 다른 컴포넌트를 렌더링하거나 네이티브 코드와 직접 통신할 수 있는 확장성도 제공한다고 한다.</p>
<hr>
<h1 id="expo와-react-native-cli">Expo와 React Native CLI</h1>
<p>React Native를 사용할 때는 두 가지 방식 중 하나를 선택할 수 있다. 하나는 <strong>Expo</strong>이고, 다른 하나는 <strong>React Native CLI</strong>이다.</p>
<ul>
<li><p><strong>Expo</strong>는 설정이 간편하고 빠르게 시작할 수 있도록 도와주는 상위 툴킷이다. (실제로 React Native <strong>공식문서에서 권장</strong>하고 있는 방식)</p>
</li>
<li><p><strong>React Native CLI</strong>는 순정 React Native 환경으로, 더 많은 유연성과 네이티브 접근성을 제공한다.</p>
</li>
</ul>
<h2 id="개발-환경-세팅">개발 환경 세팅</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>Expo</th>
<th>React Native CLI</th>
</tr>
</thead>
<tbody><tr>
<td>설치 난이도</td>
<td>매우 쉬운 편</td>
<td>상대적으로 복잡함</td>
</tr>
<tr>
<td>실행 방식</td>
<td>Expo Go 앱을 통한 QR 실행</td>
<td>Android Studio, Xcode 필요</td>
</tr>
<tr>
<td>네이티브 기능 접근</td>
<td>Expo SDK로 대부분 제공</td>
<td>직접 모듈 설치 및 네이티브 설정 필요</td>
</tr>
<tr>
<td>빌드 방식</td>
<td>EAS Build를 통한 클라우드 빌드</td>
<td>로컬 빌드 또는 CI 파이프라인 구성 필요</td>
</tr>
</tbody></table>
<h2 id="expo-장단점">Expo 장단점</h2>
<h3 id="장점">장점</h3>
<ul>
<li>초기 설정이 거의 필요하지 않아 <strong>빠르게 앱을 실행</strong>할 수 있다.</li>
<li>Expo Go 앱으로 QR 코드를 스캔하여 바로 실기기 테스트가 가능하다.</li>
<li><strong>카메라, 위치, 알림 등 자주 사용하는 네이티브 기능을 Expo SDK가 기본적으로 제공</strong>한다.</li>
<li>EAS Build를 활용하면 mac 없이도 iOS 앱을 빌드할 수 있다고 한다.</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li><strong>Expo에서 지원하지 않는 네이티브 기능은 사용할 수 없다.</strong></li>
<li>직접 만든 <strong>Swift 또는 Java 기반의 네이티브 모듈을 연동하기 어렵다.</strong></li>
<li>Expo SDK 전체가 포함되어 앱 용량이 커질 수 있다는 단점이 있다.</li>
<li>일부 네이티브 관련 오류는 디버깅이 어렵다고 한다.</li>
</ul>
<h2 id="react-native-cli-장단점">React Native CLI 장단점</h2>
<h3 id="장점-1">장점</h3>
<ul>
<li><strong>모든 네이티브 기능을 제약 없이 사용</strong>할 수 있다.</li>
<li><strong>외부 네이티브 라이브러리나 커스텀 모듈을 자유롭게 설치하고 연동</strong>할 수 있다.</li>
<li>필요한 라이브러리만 포함시켜 앱의 용량을 최적화할 수 있다.</li>
<li>기업이나 대규모 프로젝트에서는 실무 환경과 가장 유사한 방식이라고 할 수 있다.</li>
</ul>
<h3 id="단점-1">단점</h3>
<ul>
<li>Android Studio, Xcode 등의 설정이 필요해 <strong>진입 장벽이 높은 편</strong>이다.</li>
<li>초보자가 처음부터 설정하기에는 어려움이 있다.</li>
<li>네이티브 코드를 수정할 경우 빌드 시간이 길어지고, 설정 충돌이 발생할 가능성도 존재한다.</li>
</ul>
<hr>
<h2 id="결론">결론</h2>
<p>React Native를 처음 접하거나 빠르게 프로토타입을 만들고 싶다면 <strong>Expo</strong>를 사용하는 것이 적합하다. 반면 카메라 제어, 화면 공유, 블루투스 등 고급 네이티브 기능이 필요한 경우에는 <strong>React Native CLI</strong>를 사용하는 것이 바람직하다. 이번에 내가 만들어보고자 하는 앱에서는 모바일 화면을 공유하는 기능이 꼭 필요했기 때문에 나는 React Native CLI 방식으로 개발하려고 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 12865번] 평범한 배낭 - DP, 배낭 문제(KnapSack Problem), 접근 방법, 문제 풀이]]></title>
            <link>https://velog.io/@jiiker_/%EB%B0%B1%EC%A4%80-12865%EB%B2%88-%ED%8F%89%EB%B2%94%ED%95%9C-%EB%B0%B0%EB%82%AD-DP-%EB%B0%B0%EB%82%AD-%EB%AC%B8%EC%A0%9CKnapSack-Problem-%EC%A0%91%EA%B7%BC-%EB%B0%A9%EB%B2%95-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@jiiker_/%EB%B0%B1%EC%A4%80-12865%EB%B2%88-%ED%8F%89%EB%B2%94%ED%95%9C-%EB%B0%B0%EB%82%AD-DP-%EB%B0%B0%EB%82%AD-%EB%AC%B8%EC%A0%9CKnapSack-Problem-%EC%A0%91%EA%B7%BC-%EB%B0%A9%EB%B2%95-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4</guid>
            <pubDate>Fri, 14 Feb 2025 15:40:40 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-설명">문제 설명</h2>
<p>이 문제는 아주 평범한 배낭에 관한 문제이다.</p>
<p>한 달 후면 국가의 부름을 받게 되는 준서는 여행을 가려고 한다. 세상과의 단절을 슬퍼하며 최대한 즐기기 위한 여행이기 때문에, 가지고 다닐 배낭 또한 최대한 가치 있게 싸려고 한다.</p>
<p>준서가 여행에 필요하다고 생각하는 N개의 물건이 있다. 각 물건은 무게 W와 가치 V를 가지는데, 해당 물건을 배낭에 넣어서 가면 준서가 V만큼 즐길 수 있다. 아직 행군을 해본 적이 없는 준서는 최대 K만큼의 무게만을 넣을 수 있는 배낭만 들고 다닐 수 있다. 준서가 최대한 즐거운 여행을 하기 위해 배낭에 넣을 수 있는 물건들의 가치의 최댓값을 알려주자.</p>
<h2 id="입력">입력</h2>
<p>첫 줄에 물품의 수 N(1 ≤ N ≤ 100)과 준서가 버틸 수 있는 무게 K(1 ≤ K ≤ 100,000)가 주어진다. 두 번째 줄부터 N개의 줄에 거쳐 각 물건의 무게 W(1 ≤ W ≤ 100,000)와 해당 물건의 가치 V(0 ≤ V ≤ 1,000)가 주어진다.</p>
<p>입력으로 주어지는 모든 수는 정수이다.</p>
<h2 id="출력">출력</h2>
<p>한 줄에 배낭에 넣을 수 있는 물건들의 가치합의 최댓값을 출력한다.</p>
<h2 id="예제">예제</h2>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/5ffe606e-186f-45ff-9bbe-6102fa848bb6/image.PNG" width="100%"/>
</p>

<hr>
<h2 id="문제-분석">문제 분석</h2>
<p>이제 곧 546박 547일짜리 여행을 떠날텐데 그 전에 또 여행을 가려고 하는 준서. 준서가 여행 가방을 싸려고 하는데, 각각의 물건들은 무게(W)와 가치(V)를 가진다. 이 때 가방에 담은 물건들의 가치의 합이 최대가 되도록 물건을 담았을 때, 그 최댓값을 구하는 문제이다.</p>
<hr>
<h2 id="접근-방법">접근 방법</h2>
<h3 id="완전탐색">완전탐색</h3>
<p>처음에 문제를 읽으면 <strong>완전탐색</strong>을 떠올리기 쉽다. 각 물품마다 넣거나 넣지 않는 두 가지의 선택지가 존재하고, 모든 경우를 다 살펴봐야지 가치합의 최댓값을 알 수 있지 않을까? 하는 생각에 완전탐색을 시도하게 된다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/84156773-a08a-4aca-870e-6f9473114318/image.PNG" width="60%"/>
</p>

<p>항상 완전탐색을 시도하기 전엔 <strong>시간복잡도를 간단하게라도 계산</strong>해보자. N의 최댓값이 100이라 얼핏 가능할 것 같기도 하지만, 이 <strong>100개의 물건</strong>마다 <strong>넣거나 넣지 않는 두 가지의 선택지</strong>가 있고, 이 사건들은 <strong>동시에 일어나기 때문에 곱 연산</strong>을 해줘야 한다. 그렇게 되면 총 연산횟수가 대략 <strong>2의 100제곱</strong>이 된다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/a32cdf82-1c3c-4a7f-8691-3549b3ab49d0/image.PNG" width="80%"/>
</p>

<p>뭐 대략 이런 숫자라고 한다. 컴퓨터가 1초에 1억번 정도 연산한다고 했을 때 <strong>이 연산을 끝내기 위해서는 403경 년이 걸린다</strong>고 한다. 이런 <strong>이유에서 완전탐색은 넣어두도록 하자.</strong></p>
<h3 id="다이나믹-프로그래밍dp">다이나믹 프로그래밍(DP)</h3>
<p>완전탐색이나 백트래킹으로 문제를 풀다가 <strong>시간복잡도에서 문제가 생길 경우 DP로 해결가능한지 반드시 검토해 보자!</strong> 사실 나도 배낭 문제를 처음 풀었을 당시에는 혼자서 풀이법을 떠올리지 못 했다. <strong>DP라 함은 이전 상태에 기반해서 현재 상태를 유추하는 방식</strong>으로 최적해를 찾아 나가는 풀이법인데, 어떤 상태를 변수로 두고 DP를 구현해야할 지 전혀 감이 오지 않았기 때문이다.</p>
<p>기존에 풀었던 DP 문제들은 <strong>한 가지 상태를 변화</strong>시키면서 특정 상태의 최적해를 가지고서 현재 상태의 최적해를 구하는 점화식을 구현해서 풀었다. 하지만, <strong>배낭 문제(KnapSack Problem) 유형이라고도 불리는 이 문제에서는 두 가지 상태를 변수로 두고, 이를 변화시키며 최적해를 찾아</strong> 나간다. 그렇기 때문에 <strong>DP 배열이 2차원 배열</strong>로 만들어지고, <strong>2차원 DP</strong>라고도 불린다.</p>
<h3 id="배낭-문제knapsack-problem---2차원-dp">배낭 문제(KnapSack Problem) - 2차원 DP</h3>
<p>배낭 문제를 처음 접하면, <strong>어떤 상태를 변수로 둬야 할지</strong> 감이 오지 않는 경우가 많다. 이 부분에 대해서 어떻게 떠올릴 수 있을지 나름의 방법을 고민 해보고 적도록 하겠지만, 이해가 전혀 가지 않는다면 결론만 외워도 좋을 것 같다.</p>
<p>우리는 <strong>특정 물건들</strong>로 <strong>가방을 채워</strong> 만들 수 있는 <strong>최대 가치를 저장</strong>해야 한다. 그렇다면 우리가 특정한 상황, 즉 <strong>가방에 일부 물건이 채워진 상태에 놓여 있다고 가정</strong>해 보자. 이때 현재 가방에 담긴 가치가 최적해인지 판단하려면, 가장 먼저 <strong>남은 물건 중에서 빈 공간에 더 넣을 수 있는 물건이 무엇인지 확인하는 과정이 필요</strong>할 것이다.</p>
<p>여기서 우리는 힌트를 얻을 수 있다. <strong>빈 공간에 최적의 가치를 담는 방법</strong>이 존재한다면, 이를 활용해 더 나은 결과를 만들 수 있다. 그렇기 때문에 첫 번째 상태 변수는 <strong>&#39;가방의 최대 무게&#39;</strong>이다. 가방의 최대 무게를 작은 값부터 점점 증가시키면서, <strong>특정 무게에서 만들 수 있는 최적의 가치를 저장</strong>해 두면, 이후 더 큰 무게에서도 이 값을 활용할 수 있다. 이러한 방식으로 현재 상태를 이전 상태를 기반으로 확장할 수 있게 된다.</p>
<blockquote>
<p>w=0 → 배낭을 아예 사용하지 않은 상태
w=1 → 최대 무게가 1 kg일 때 최적해
w=2 → 최대 무게가 2 kg일 때 최적해
…
w=K → <strong>최대 무게가 K kg일 때 최적해</strong></p>
</blockquote>
<p>또한, 특정 물건을 가방에 담았을 때 남은 공간을 활용하려면, <strong>이전 상태가 해당 물건을 고려하지 않은 상태여야만 한다.</strong> 즉, <strong>현재 물건을 포함한 상태와 포함하지 않은 상태를 구분</strong>해야 한다. 이를 위해 두 번째 상태 변수로 <strong>‘물건의 종류’</strong>를 사용한다. 특정 물건까지 고려한 경우와 고려하지 않은 경우를 따로 관리하면, 현재까지의 최적해를 저장하고 이를 바탕으로 다음 단계를 계산할 수 있다. 여기서 조금 <strong>특이한 점은 특정 물건을 고려하는 단계에서 남은 공간을 활용할 때 그 물건을 제외한 모든 물건을 고려하지는 않는다는 점</strong>이다. 두 번째 물건의 경우 남은 공간에 첫 번째 물건까지 고려한 최적해를 이용하고, 세 번째 물건의 경우 남은 공간에 두 번째 물건까지 고려한 최적해를 이용하는 방식으로 확장하더라도 <strong>결국 최종적으로는 모든 물건을 고려한 최적 상태에 도달할 수 있다는 점</strong>을 이해해야 한다.</p>
<blockquote>
<p>i=0 → 아무것도 고려하지 않은 상태
i=1 → 첫 번째 물건까지 고려
i=2 → 두 번째 물건까지 고려
…
i=N → <strong>모든 물건을 고려한 최적 상태</strong></p>
</blockquote>
<p>결국, 배낭 문제에서는 <strong>‘가방의 무게’</strong>와 <strong>‘현재까지 고려한 물건의 종류’</strong>를 상태 변수로 설정하는 것이 핵심이다.</p>
<hr>
<h2 id="문제-풀이javascript-nodejs">문제 풀이(JavaScript, Node.js)</h2>
<h3 id="dp-배열-선언">DP 배열 선언</h3>
<pre><code class="language-js">const dp = Array.from({ length: N + 1 }, () =&gt; Array(W + 1).fill(0));</code></pre>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/40039833-753c-41a3-8dd7-b299f5874a8a/image.PNG" width="80%"/>
</p>

<p>2차원 DP 배열을 선언하고, 각 칸의 값은 <strong>특정 물건(i)까지 존재할 때, 특정 가방 무게(w)를 채울 수 있는 최대 가치를 의미</strong>한다. 첫 번째 줄을 채울 때 예외처리를 따로 하지 않으려면 각각 <strong>0번째 줄(아무 물건도 없는 경우, 가방 무게가 0인 경우)을 고려</strong>해서 N+1, W+1 크기의 배열로 선언하는 것이 좋다.</p>
<h3 id="dp-배열-채우기">DP 배열 채우기</h3>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/7758c1bc-48b9-49ae-ab07-1f02778fb83c/image.PNG" width="90%"/>
</p>

<p>각각의 칸을 채우면서 <strong>두 가지 값을 비교</strong> 해줘야 한다. </p>
<p><strong>현재 물건을 넣지 않는다면,</strong> 현재 가방 최대 무게가 <strong>그대로(<code>w</code>)</strong> 남게 되고, 그 <strong>남은 부분을 채울 값은 현재 물건을 고려하지 않는 시점(<code>i-1</code>)으로부터 가져와야</strong> 한다.</p>
<pre><code class="language-js">dp[i - 1][w]</code></pre>
<p><strong>현재 물건을 넣는다면,</strong> 현재 가방 최대 무게에서 <strong>현재 물건의 무게 많큼 뺀 부분(<code>w - weights[i]</code>)</strong>을 채워넣어야 하고, 마찬가지로 <strong>남은 부분을 채울 값은 현재 물건을 고려하지 않는 시점(<code>i-1</code>)으로부터 가져와야</strong> 한다.</p>
<pre><code class="language-js">values[i] + dp[i - 1][w - weights[i]]</code></pre>
<p>가방의 최대 무게를 고려했을 때, <strong>현재 물건을 넣을 수 없다면 바로 전자의 값을 가져오면 되는 것</strong>이고, <strong>현재 물건을 넣을 수 있다면 두 경우를 비교해서 더 큰 값을 최적해</strong>로 하면 된다.</p>
<pre><code class="language-js">// 현재 물건을 넣지 못 하는 경우(이전 최적해 그대로 가져옴)
dp[i][w] = dp[i - 1][w];

// 현재 물건을 넣을 수 있는 경우
if (w &gt;= weights[i]) {
    dp[i][w] = Math.max(dp[i - 1][w], values[i] + dp[i - 1][w - weights[i]]);
}</code></pre>
<h3 id="결과">결과</h3>
<p>이렇게 하면, <strong>최종적으로 다음과 같이 2차원 DP 배열을 채울 수 있다.</strong></p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/468ccabb-a498-463d-a824-8d7f64f7cb39/image.PNG" width="90%"/>
</p>


]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript, Node.js] JS로 코딩테스트 준비하기!]]></title>
            <link>https://velog.io/@jiiker_/JavaScript-Node.js-JS%EB%A1%9C-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A4%80%EB%B9%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jiiker_/JavaScript-Node.js-JS%EB%A1%9C-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A4%80%EB%B9%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 08 Feb 2025 12:00:41 GMT</pubDate>
            <description><![CDATA[<p><strong>프론트엔드 개발자</strong>로 취업을 준비하고 있지만, 코딩테스트를 볼 때 <strong>JavaScript를 사용하지는 않았었다.</strong> <strong>JavaScript가 알고리즘 문제를 풀기에 그렇게 좋은 언어가 아니라</strong>는 얘기를 많이 들었었고, 처음 공부를 C로 시작했기 때문에 C++을 사용하는 것이 훨씬 편해서 특별히 바꿔야겠다는 생각을 하지 않았었다. 백준 문제를 풀 때, <strong>JavaScript의 경우 표준 입력을 받는 부분부터 직관적이지 않았기 때문</strong>에 거부감도 있었던 것 같다. 특히, 최근에 본 코딩 테스트들에서 자주 등장했던 문제 유형 중 하나가 우선순위 큐를 이용하는 문제였는데, <strong>JavaScript에서는 우선순위 큐를 직접 구현해야 하는 불편함</strong>도 걸림돌 중에 하나였다.</p>
<p>가끔, JavaScript만 사용 가능한 코테가 있으면, 시험 보기 직전에 <strong>스택(Stack), 큐(Queue)는 어떻게 사용</strong>해야 하고, <strong>Map이나 Set 같은 자료구조는 어떻게 사용하는지</strong> 정도 체크했던 것 같다. 그리고 <strong>특정 크기의 배열을 선언하고, 특정값으로 초기화 하는 것</strong> 정도를 공부했었다. 그런데 요즘 들어서 JavaScript로 코테를 봐야하는 상황이 자주 생기는 것 같아서 이번 기회에 <strong>기본적인 것들을 정리해 보고자</strong> 이 글을 쓴다.</p>
<h1 id="⌨️-javascript로-표준-입력-받기">⌨️ JavaScript로 표준 입력 받기</h1>
<p>이 부분은 사실 알고리즘 문제 풀이를 하기 위해서는 기본적인 부분인데, <strong>프로그래머스 환경에서 시험을 보는 경우 이 부분이 생략</strong>되기 때문에 대비를 좀 덜 하게 되는 부분이 있다. 그리고 나의 경우 C++에서 <code>cin &gt;&gt; var;</code> 과 같이 단순하게 input을 처리하다가 이 코드를 봤을 땐 정말 이게 뭔가 싶었다. 하지만, 작년에 귀찮다고 계속 공부를 안하다가 JavaScript 코테에서 표준 입력을 받아서 처리해야 하는 문제가 나와서 시험시작 1분만에 종료 버튼을 눌렀던 경험이 있었다. 코드를 자세히 뜯어보면 그렇게 어려운 코드도 아니니 <strong>JavaScript로 코딩테스트를 준비한다면 한 번 쯤 익혀두는 것이 좋다.</strong></p>
<p>보통 코딩테스트에서 제출하는 JavaScript 코드는 <strong>Node.js 환경에서 실행되기 때문에 Node.js 모듈을 사용해서 표준 입력을 처리</strong>해줄 수 있다. 오늘 오전에 치룬 현대오토에버 코딩테스트 같은 경우에는 레퍼런스로 JavaScript 문서가 아닌 <strong><a href="https://nodejs.org/docs/latest-v12.x/api/fs.html">Node.js 문서</a>를 제공</strong>해주는데, 해당 링크에서 <strong>File system 모듈이나 Readline 모듈의 사용 예제 등</strong>을 찾아볼 수 있었다.</p>
<h2 id="fs-모듈을-사용하는-방법">fs 모듈을 사용하는 방법</h2>
<pre><code class="language-js">const fs = require(&quot;fs&quot;);

// 표준 입력 전체를 동기적으로 읽어옴
const input = fs.readFileSync(&quot;/dev/stdin&quot;).toString().trim().split(&quot;\n&quot;);</code></pre>
<p><strong>백준 사이트에서 안내해주는 입출력 처리 예시도 fs 모듈을 사용한 방법으로 안내</strong>가 되어있고, fs 모듈을 사용하는 쪽이 <strong>좀 더 직관적으로 와닿는 것 같아서 나는 이 방식을 선택</strong>했다. 아무래도 toString(), trim(), split()과 같은 메서드가 좀 더 직관적으로 의미가 와닿기 때문인 것 같다.</p>
<p>코드를 보면, fs(파일 시스템) 모듈을 불러온 다음에 해당 모듈을 이용해서 <code>/dev/stdin</code> <strong>경로에 있는 파일을 동기적으로 읽어온다.</strong> linux 환경에서는 콘솔로부터의 입력을 해당 경로에 저장하기 때문에 이렇게 처리해 준다. window 환경을 고려한다면,</p>
<pre><code class="language-js">readFileSync(process.platform === linux ? &quot;/dev/stdin&quot; : &quot;./input.txt&quot;)</code></pre>
<p>와 같이 처리해 줄 수도 있지만, 대부분의 코딩테스트 채점 환경은 linux이기 때문에 <code>&quot;/dev/stdin&quot;</code>만 작성해 줘도 무방하다.</p>
<p>그 이후는 <strong>입력을 문자열로 바꾸어주고</strong>(<code>toString()</code>), <strong>앞 뒤 공백을 제거</strong>해주고(<code>trim()</code>), <strong>줄바꿈을 기준으로 나누어 배열에 담아준다</strong>(<code>split(&quot;\n&quot;)</code>).</p>
<h2 id="readline-모듈을-사용하는-방법">readline 모듈을 사용하는 방법</h2>
<pre><code class="language-js">const readline = require(&quot;readline&quot;);

const rl = readline.createInterface({
  input: process.stdin,   // 표준 입력
  output: process.stdout, // 표준 출력
});

let lines = [];

rl.on(&quot;line&quot;, (line) =&gt; {
  lines.push(line);
}).on(&quot;close&quot;, () =&gt; {
  // 입력이 끝났을 때 실행되는 부분
  // 여기에 solution 작성하면 됨!!
  process.exit();
});</code></pre>
<p>과거의 나는 이 코드를 보고 뒤로가기를 눌렀던 것 같다. <strong>이 코드에서 거부감이 든다면, fs 모듈을 사용하는 것으로 하고, 이번 섹션은 살포시 건너뛰도록 하자.</strong> 당시에는 이벤트를 발생(emit)시키고, 감지(listen)하는 코드를 작성해 본 적이 없었지만, 부스트캠프에서 Node.js로 이벤트리스너를 이용해서 이것저것 구현하다보니 이제는 조금 익숙한 코드가 되었다.</p>
<p>모듈 이름에서 어느 정도 유추할 수도 있는데, fs(파일 시스템) 모듈은 콘솔 입력이 담긴 파일을 readFileSync로 읽어서 활용하는 방식이었고, <strong>readline 모듈은 말 그대로 콘솔 입력을 한 줄씩 읽어주는 방식</strong>이다. <code>createInterface()</code>를 통해 어디서 입력을 받아 어디로 출력 해줄지를 정한다. <code>process.stdin</code>과 <code>process.stdout</code>이 각각 표준 입출력을 의미하고, 이를 각각 <code>input</code>, <code>output</code>으로 설정해준다.</p>
<p>그렇게 하면, <strong>readline 인터페이스에서 표준 입력을 한 줄 읽을 때마다 &quot;line&quot; 이벤트를 발생</strong> 시키고, 그 때마다 <code>rl.on(&quot;line&quot;, (line) =&gt; {</code> 부분에서 <strong>&quot;line&quot; 이벤트를 감지</strong>하게 된다.</p>
<pre><code class="language-js">rl.on(&quot;line&quot;, (input) =&gt; {})</code></pre>
<p>이는 위에서 생성한** readline 인터페이스가 켜져있고<strong>(<code>.on()</code>), **line 이벤트를 기다리고 있는데</strong>(<code>&quot;line&quot;</code>), 이 <strong>이벤트가 감지되면 두 번째 인자의 함수</strong>(<code>(input) =&gt; {}</code>)<strong>를 실행</strong>시킨다는 의미이다. 이 때의 <strong>input 자리에는 readline 인터페이스가 이벤트를 발생시킬 때 읽었던 한 줄이 전달</strong>된다.</p>
<pre><code class="language-js">let lines = [];

rl.on(&quot;line&quot;, (line) =&gt; {
  lines.push(line);
})</code></pre>
<p><strong>&quot;line&quot; 이벤트 때 실행되는 함수와 &quot;close&quot; 이벤트 때 실행되는 함수는 엄연히 다른 함수</strong>이기 때문에 바로 line 값을 활용할 수는 없다. 그렇기 때문에 바깥에 <strong>전역적으로 lines 배열을 선언하고 이 배열을 활용</strong>해야 한다. </p>
<pre><code class="language-js">.on(&quot;close&quot;, () =&gt; {

    // lines 배열을 이용해서 문제 풀이 ✏️

    process.exit();
})</code></pre>
<p>마찬가지로 <strong>입력이 끝나면 &quot;close&quot; 이벤트를 발생</strong>시키고, <strong>해당 이벤트는 위의 코드에서 감지</strong>하게 된다. 입력이 끝났고, 해당 입력을 이용한 <strong>문제 풀이를 두 번째 인자 함수 내부에 작성해주면 된다.</strong> 여기서 <strong>주의할 점</strong>은 이 입력을 받는 과정이 <strong>비동기로 처리</strong>되기 때문에 <strong>반드시 해당 함수 내부에 문제 풀이를 적거나 내부에서 문제 풀이 함수를 실행시켜야 한다.</strong></p>
<h1 id="📚-javascript에서-여러가지-자료구조-사용법">📚 JavaScript에서 여러가지 자료구조 사용법</h1>
<p>이전에 C++로 코딩테스트를 준비할 때도 특별히 복잡한 자료구조를 사용하지는 않았다. <strong>배열(Array)는 필수적</strong>으로 사용할 줄 알아야 하고, 알고리즘 구현에 있어서 <strong>스택(Stack)이나 큐(Queue)도 거의 필수적</strong>으로 알고 있어야 한다. 다음으로 작년 코딩테스트에서 자주 등장했던 것 같은데, <strong>해시 테이블(Hash Table) 기반의 맵(Map)과 셋(Set) 자료구조 또한 알고 있으면 좋다.</strong> 그 이외에 다익스트라 알고리즘(Dijkstra&#39;s Algorithm)을 구현할 때 사용되기도 하고, 독립적으로 문제에 등장하기도 하는 <strong>우선순위 큐(Priority Queue)</strong>도 알고 있으면 좋긴 하지만, <strong>JavaScript에서는 우선순위 큐를 제공 해주는 라이브러리가 없기 때문에 직접 구현해야 하는데, 그렇게 간단하지는 않아서 JavaScript 코딩테스트에서는 등장하기 어려운 주제</strong>일 것으로 생각된다.</p>
<h2 id="array">Array</h2>
<p>배열을 선언하는 방법은 여러가지가 있고, <strong>편한 방식을 사용</strong>하면 된다.</p>
<h3 id="1차원-배열-선언-및-초기화">1차원 배열 선언 및 초기화</h3>
<pre><code class="language-js">const arr = new Array(5).fill(0);
console.log(arr); // [0, 0, 0, 0, 0]</code></pre>
<p>배열의 크기를 5로 선언하고, 모든 요소를 0으로 초기화하는 코드이다.</p>
<pre><code class="language-js">const arr = new Array(3).fill([]);
arr[0].push(1);
console.log(arr); // [[1], [1], [1]] ❗주의❗ (모두 같은 배열 참조)

const arr = new Array(3).fill(null).map(() =&gt; []);
arr[0].push(1);
console.log(arr); // [[1], [], []]</code></pre>
<p>이 방식으로 선언할 때 <strong>조심해야할 것</strong>은 만약에 <strong>배열([ ])로 초기화</strong> 하게 되는 경우, 모든 요소에 같은 배열이 담기게 되어서 한 배열이 변경되면 모든 배열의 값이 변경되게 된다. 따라서 <strong>이 방식으로 2차원 배열을 선언할 때는 map()을 이용해서 배열을 넣어주자.</strong></p>
<pre><code class="language-js">const arr = Array.from({ length: 5 }, () =&gt; 0);
console.log(arr); // [0, 0, 0, 0, 0]

const arr = Array.from({ length: 5 }, (_, i) =&gt; i + 1);
console.log(arr); // [1, 2, 3, 4, 5]</code></pre>
<p><code>Array.from()</code> <strong>메서드를 이용해서 선언하는 방식</strong>도 있다. <strong>첫 번째 인자로 length</strong>를 넘겨주고, <strong>두 번째 인자의 함수에서 리턴해주는 값으로 초기화</strong> 하는 방식이다. 이렇게 선언하면 두 번째 인자 화살표 함수에서 인덱스를 활용해 순차적으로 값이 증가하도록 배열을 초기화 할 수도 있다.</p>
<h3 id="2차원-배열-선언-및-초기화">2차원 배열 선언 및 초기화</h3>
<pre><code class="language-js">const matrix = Array.from({ length: 3 }, () =&gt; new Array(3).fill(0));
console.log(matrix);
/*
[
  [0, 0, 0],
  [0, 0, 0],
  [0, 0, 0]
]
*/</code></pre>
<p>위에서 봤던 <strong>방식을 섞어서 2차원 배열을 선언</strong>할 수도 있고,</p>
<pre><code class="language-js">const matrix = Array.from({ length: 3 }, () =&gt;
  Array.from({ length: 3 }, () =&gt; 0)
);

console.log(matrix);
/*
[
  [0, 0, 0],
  [0, 0, 0],
  [0, 0, 0]
]
*/</code></pre>
<p><strong>한 가지  방법만으로 선언</strong>할 수도 있다. 위에서 말했던 <code>fill()</code> <strong>메서드를 활용한 방식에서 배열로 초기화할 때만 주의</strong>해서 사용하도록 하자!</p>
<h2 id="stack">Stack</h2>
<pre><code class="language-js">const stack = [];

stack.push(10);
stack.push(20);

console.log(stack.pop()); // 20
console.log(stack.pop()); // 10</code></pre>
<p>다음으로 <strong>스택(Stack)과 큐(Queue)는 오히려 다른 언어들보다 쉬울 수도</strong> 있다. 배열을 그대로 활용하는 방법인데 배열 자체에 <code>pop()</code> <strong>메서드가 존재해서 스택처럼 사용</strong>이 가능하다. <code>stack.top()</code>을 확인하고 싶을 땐, <code>stack[stack.length - 1]</code><strong>과 같은 방식으로 확인</strong>할 수 있다.</p>
<h2 id="queue">Queue</h2>
<pre><code class="language-js">const queue = [];

queue.push(10);
queue.push(20);

console.log(queue.shift()); // 10
console.log(queue.shift()); // 20</code></pre>
<p>배열의 첫 번째 요소를 제거하는 것은 <code>shift()</code> <strong>메서드를 이용해서 가능</strong>하다. 이를 통해 배열을 큐처럼 사용할 수 있고, 마찬가지로 <code>queue.front()</code>를 확인하고 싶으면, <code>queue[0]</code><strong>을 이용해서 확인</strong>하도록 하자!</p>
<h2 id="map">Map</h2>
<pre><code class="language-js">const myMap = new Map();

myMap.set(&#39;key1&#39;, &#39;value1&#39;);
myMap.set({ a: 1 }, &#39;objectKey&#39;); // 객체를 키 값으로 저장하는 것도 가능

console.log(myMap.get(&#39;key1&#39;));   // &#39;value1&#39;
console.log(myMap.has(&#39;key1&#39;));   // true
myMap.delete(&#39;key1&#39;);
console.log(myMap.has(&#39;key1&#39;));   // false</code></pre>
<p><strong>맵(Map)은 키(Key)와 값(Value) 형태로 데이터를 저장하는 자료구조</strong>로, 내부적으로 <strong>해시 테이블</strong>을 사용해 키를 해시 함수로 매핑한다. 이 덕분에 <strong>키를 사용해 값의 탐색, 삽입, 삭제를 시간복잡도 <em>O(1)</em> 에 처리</strong>할 수 있다.</p>
<h2 id="set">Set</h2>
<pre><code class="language-js">const mySet = new Set();

mySet.add(1);
mySet.add(2);
mySet.add(2); // 중복이므로 추가되지 않음

console.log(mySet.size);   // 2
console.log(mySet.has(2)); // true
mySet.delete(2);
console.log(mySet.has(2)); // false</code></pre>
<p><strong>셋(Set) 또한 해시 테이블 기반의 자료구조</strong>로 키 값은 없이 <strong>값(Value)만 저장하는 형태의 자료구조</strong>이다. 특징적인 것은 <strong>중복을 허용하지 않기 때문에</strong> 이미 존재하는 값을 추가(add)하게 되면 그 값은 무시되고 추가되지 않는다. 맵과 마찬가지로 <strong>탐색, 삽입, 삭제에 있어서 시간복잡도는 <em>O(1)</em> 이다.</strong></p>
<h2 id="🧐-그-외에-알아두면-유용한-메서드">🧐 그 외에 알아두면 유용한 메서드</h2>
<p>간혹 기본 구현 문제에서 문자열을 다루는 문제가 나왔을 때 <code>split()</code>, <code>join()</code>, <code>slice()</code>, <code>splice()</code>,  메서드들을 잘 다루면 굉장히 유용할 때가 있다.</p>
<h3 id="split">split()</h3>
<pre><code class="language-js">const sentence = &quot;Hello World from JavaScript!&quot;;
const words = sentence.split(&quot; &quot;);  // 공백을 기준으로 분리
console.log(words);  // [&quot;Hello&quot;, &quot;World&quot;, &quot;from&quot;, &quot;JavaScript!&quot;]

const sentence = &quot;Hello&quot;;
const chars = sentence.split(&quot;&quot;);  // 빈 문자열을 구분자로 사용하여 각 문자로 분리
console.log(chars);  // [&quot;H&quot;, &quot;e&quot;, &quot;l&quot;, &quot;l&quot;, &quot;o&quot;]</code></pre>
<p><code>split()</code> 메서드는 위에서 표준 입출력을 처리하는 부분에서도 사용했듯이 <strong>특정 문자 혹은 문자열을 구분자로 해서 문자열을 분할</strong>하고, <strong>분할된 문자열을 배열에 담아 반환</strong>하는 메서드이다. <code>split()</code> 메서드의 <strong>괄호안에 아무것도 넣지 않으면 단순히 배열에 문자열을 그대로 넣어 반환</strong>해주기 때문에 <strong>한 글자씩 배열에 나눠 담고 싶다면 빈 문자열(&quot;&quot;)을 구분자로</strong> 지정해주자.</p>
<h3 id="join">join()</h3>
<pre><code class="language-js">const sentence = &quot;Hello World from JavaScript!&quot;;
const words = sentence.split(&quot; &quot;);  // 공백을 기준으로 분리
console.log(words);  // [&quot;Hello&quot;, &quot;World&quot;, &quot;from&quot;, &quot;JavaScript!&quot;]

const joinedSentence = words.join(&quot;-&quot;);  // 배열의 요소를 &quot;-&quot;로 연결
console.log(joinedSentence);  // &quot;Hello-World-from-JavaScript!&quot;</code></pre>
<p><code>join()</code> 메서드는 <code>split()</code><strong>과 반대</strong>로 <strong>특정 문자 혹은 문자열을 구분자로 해서 배열에 있는 문자열들을 합쳐</strong>서 <strong>하나의 문자열로 만들어 반환</strong>하는 메서드이다. <code>join()</code> 메서드의 <strong>괄호안에 아무것도 넣지 않으면 기본값은 쉼표(,)</strong>이다.</p>
<h3 id="slice">slice()</h3>
<pre><code class="language-js">// 배열에서 일부 요소 추출
const arr = [1, 2, 3, 4, 5];
const newArr = arr.slice(1, 4);  // 인덱스 1부터 4 이전까지의 요소를 반환
console.log(newArr);  // [2, 3, 4]

// 문자열에서 일부 추출
const str = &quot;Hello, World!&quot;;
const substr = str.slice(7, 12);  // 인덱스 7부터 12 이전까지의 문자
console.log(substr);  // &quot;World&quot;</code></pre>
<p><code>slice()</code> 메서드는 <strong>문자열의 일부를 추출하여 새로운 배열이나 문자열을 반환</strong>하고, <strong>원본 배열이나 문자열을 변경하지는 않는다.</strong></p>
<h3 id="splice">splice()</h3>
<pre><code class="language-js">// 배열에서 요소 제거
const arr = [1, 2, 3, 4, 5];
const removed = arr.splice(2, 2);  // 인덱스 2부터 2개 요소를 제거
console.log(arr);     // [1, 2, 5]
console.log(removed); // [3, 4]</code></pre>
<p><code>splice()</code> 메서드는 <strong>배열에서 요소를 추가하거나 제거할 때 사용</strong>한다. <strong>원본 배열을 직접 변경</strong>하며, <strong>제거된 부분을 반환</strong>한다.</p>
<pre><code class="language-js">arr.splice(startIdx, deleteCount, item1, item2, ..., itemN);

// 배열에서 요소 추가
const arr = [1, 2, 5];

arr.splice(2, 0, 3, 4);  // 인덱스 2에 3, 4 추가
console.log(arr);        // [1, 2, 3, 4, 5]

const arr = [1, 2, 5, 6];
const result = arr.splice(2, 1, 3, 4);

console.log(result);  // [5]
console.log(arr);     // [1, 2, 3, 4, 6]</code></pre>
<p><code>splice()</code> 메서드는 <strong>첫 번째 인자로 배열에서 제거할 부분의 시작 인덱스</strong>를 받고, <strong>두 번째 인자로 해당 인덱스부터 몇 개를 제거할 것인지</strong>를 받는다. <strong>세 번째 인자부터는 제거된 자리에 추가할 요소</strong>들을 받는다. 즉, <code>splice()</code>는 배열에서 요소를 제거하거나 추가하거나 둘 다 동시에 수행할 수 있는 메서드이다.</p>
<h1 id="✍️-글을-마치며">✍️ 글을 마치며...</h1>
<p>매번 JavaScript 코딩테스트를 준비할 때마다 이 내용을 공부하는 것 같습니다. 매번 새롭게 찾아보는 것이 번거롭기도 하고, 그렇다고 또 찾아보지 않으면 은근히 헷갈리는 부분들이 많아서 이렇게 한 번에 정리를 해봤습니다! 앞으로는 시험 준비할 때 이 글 한 번 정독하면서 시작하려구요! 이번에 코딩테스트를 준비하면서 노션에 메모해 둔 내용을 어찌저찌 짜깁기만 해놓아서 보기 어려울수도 있지만, JavaScript 코딩테스트를 처음 준비하시는 분이라면 도움이 될만한 내용들이 많기 때문에 한 번쯤 읽어보면 좋을 것 같습니다! 자스코테 준비하시는 분들 화이팅입니다~!!😀</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js & Firebase] 면접 스터디 앱 하루만에 딸깍 만들기!]]></title>
            <link>https://velog.io/@jiiker_/Next.js-Firebase-%EB%A9%B4%EC%A0%91-%EC%8A%A4%ED%84%B0%EB%94%94-%EC%95%B1-%ED%95%98%EB%A3%A8%EB%A7%8C%EC%97%90-%EB%94%B8%EA%B9%8D-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@jiiker_/Next.js-Firebase-%EB%A9%B4%EC%A0%91-%EC%8A%A4%ED%84%B0%EB%94%94-%EC%95%B1-%ED%95%98%EB%A3%A8%EB%A7%8C%EC%97%90-%EB%94%B8%EA%B9%8D-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Wed, 05 Feb 2025 13:49:26 GMT</pubDate>
            <description><![CDATA[<h2 id="👨🔧-내-껀-내가-만들어-쓰자">👨‍🔧 내 껀 내가 만들어 쓰자!</h2>
<p>작년초 면접 스터디를 할 당시에 스터디를 결성하셨던 분께서 직접 면접 스터디용 앱을 직접 만들어 주셨다. 당시에 코딩 경력이 1년 남짓 되는 코린이였기에(물론 아직도..) &#39;와.. 이런 걸 직접 만들 생각을 한다고?&#39;, <strong>&#39;나도 언젠가 필요한 걸 직접 만들어서 써봐야겠다!&#39;</strong>라는 생각을 했었던 것이 기억난다. 그러다 마침 이번에 상반기 취업 준비를 위한 면접 스터디를 진행하게 되었다. 처음에는 작년에 사용했던 앱을 그대로 쓸까 생각했지만, 몇 가지 문제가 있었다.</p>
<blockquote>
<ul>
<li>당시 면접 스터디 진행 방식에 맞춰져 있는 기능</li>
<li>프론트만 구현되어 있었기 때문에 질문을 목데이터로 만들어서 수동으로 관리해야되는 점</li>
<li>편의상 로컬에서 사용해야 했기 때문에 누군가 항상 로컬에서 실행시킨 뒤에 화면공유로 진행해야 하는 점</li>
<li><del>그리고 무엇보다 가오가 상하지 않는가</del></li>
</ul>
</blockquote>
<p>1년의 시간이 흐른만큼 내가 성장했다면 그 때 그 분이 만드셨던 것보다 더 뛰어난 앱을 만들어내야 하지 않겠는가! 그런 생각이 들어서 스터디원들에게 내가 직접 만들어 오겠다고 호언장담을 해버렸다. 물론, 하루만에 만들 생각은 없었고, 설날 연휴를 이용하여 아주 기깔난 앱을 만들어 오리라 다짐했었다. 그런데 그게 하루가 되었다. <del>민족 대명절 설은 쉬는 게 맞다.</del> 그래도 하루만에 만들었기 때문에 이 몹쓸 UI의 앱에도 나름의 경쟁력이 생겼다. 기존에 사용했던 앱은 사실 누가봐도 아기자기하고 깔끔한 디자인을 갖추고 있었기에 시간마저 오래 걸렸으면 제목에 쓸 말이 없었을 것 같다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/df6d87bc-fa6a-4ea6-9295-2e012abb4e90/image.PNG" width="50%"/>
</p>

<p>이 투박하면서도 <strong>기능을 중시한 직관적인 디자인</strong>의 버튼을 보고 있으면, 정말 가슴이 웅장해지는 것 같다. <strong>누가 봐도 질문 목록으로 돌아갈 수 있을 것만 같다.</strong></p>
<h2 id="🚀-초패스트-4분-기획">🚀 초패스트 4분 기획</h2>
<p>다음 면접 스터디 전까지 만들어 가겠다고 말을 해놨기 때문에 어찌저찌 컴퓨터를 키고 구현할 것들을 정리해 봤다. 총 세 페이지로 구현할 생각이었고, <strong>면접 질문 목록 페이지, 면접 목록 페이지, 면접 진행 페이지</strong>가 필요했다.</p>
<h3 id="면접-질문-목록-페이지">면접 질문 목록 페이지</h3>
<blockquote>
<ul>
<li>각각의 질문들을 카테고리별로 분류 - <strong><em>MVP</em></strong></li>
<li>이미 진행했던 질문들 완료되었음을 표시할 수 있어야 함 - <strong><em>MVP</em></strong></li>
<li>질문 추가 삭제가 자유로워야 함 - <strong><em>MVP</em></strong></li>
</ul>
</blockquote>
<p>스터디의 진행 방식은 <strong>질문 목록에서 새로운 질문들을 랜덤하게 뽑고</strong>, 다음 스터디까지 해당 질문에 대한 답변을 공부해와서 답변하는 방식으로 진행된다. 따라서 <strong>이미 진행했던 질문은 다음 번에 질문을 뽑을 땐 제외시킬 필요</strong>가 있어서 <strong>질문 목록은 완료 표시를 할 수 있는 체크리스트로 구현</strong>하려고 생각했다. 또한 우리가 모든 질문을 다 완벽하게 대비할 수는 없기 때문에 항상 <strong>추가 삭제는 자유로워야 하는 점</strong>을 고려했다.</p>
<h3 id="면접-목록-페이지">면접 목록 페이지</h3>
<blockquote>
<ul>
<li>면접 질문을 뽑으면 해당 질문 리스트가 저장되어 있어야 함 - <strong><em>MVP</em></strong></li>
<li>완료된 면접과 진행중 면접을 구분할 수 있어야 함 - <em>선택</em></li>
<li>면접 카드만 보고 어떤 질문을 진행했던 면접인지 알 수 있으면 좋음 - <em>선택</em></li>
</ul>
</blockquote>
<p>스터디 진행 방식상 면접 <strong>질문을 선택하고 해당 질문들에 대한 모의 면접을 진행하기까지는 3일 정도의 텀</strong>이 있다. 그렇다면 질문을 고르는 순간 골라진 질문 목록을 저장해 둘 필요가 있었다. 그리고 <strong>스터디 활동 내용을 기록</strong>하는 차원에서도 데이터가 남아있으면 좋을 것 같다는 생각을 했다. 그리고 당연히 제일 최신 면접 질문이 현재 진행해야할 질문들이 되겠지만, 시각적으로 확실하게 <strong>완료된 면접과 진행중인 면접을 구분</strong>할 수 있으면 좋을 것 같았고, 마찬가지로 <strong>해당 면접에 어떤 질문들이 포함되어 있는지</strong> 간략하게라도 알 수 있으면 좀 더 <strong>편의성 측면</strong>에서 헷갈리지 않고 좋을 것 같았다.</p>
<h3 id="면접-진행-페이지">면접 진행 페이지</h3>
<blockquote>
<ul>
<li>면접 참여자를 입력으로 받아서 진행 - <strong><em>MVP</em></strong></li>
<li>각 질문에 참여자 랜덤 배정 - <strong><em>MVP</em></strong></li>
<li>각 질문 마다 해당 면접을 진행하며 물어봤던 꼬리질문이나 추가적으로 얘기를 나눴던 부분들을 댓글 형태로 남길 수 있으면 좋음 - <em>선택</em></li>
</ul>
</blockquote>
<p><strong>매 스터디 마다 5개의 기술 질문과 1개의 기본 질문(인성, 경험)</strong>을 골라 준비하기로 했다. 그런데 스터디 진행할 때 모든 사람이 모든 질문에 답변하는 방식으로 진행하면 시간이 너무 오래 걸려서 <strong>기본 질문은 모두가 답변하는 것으로 하고, 기술 질문 5개 중 2개씩 랜덤으로 뽑아서 답변</strong>하기로 했다. <strong>이 부분을 알고리즘으로 구현</strong>하는 것이 정말 별 거 아닌 것 처럼 보였었는데, 면접 진행 방식과 맞물려서 많은 골칫거리를 제공하기도 했다. 그리고 <strong>질문에 답변하는 사람 외에 다른 사람 모두가 꼬리 질문</strong>을 하기 때문에 꼬리 질문을 하며 추가적으로 알게 되는 내용들도 많았는데, <strong>이런 내용들을 기록할 수 있는 댓글 기능</strong>이 있으면 좋을 것 같았다.</p>
<h2 id="🙋♂️-어서와-백엔드는-처음이지">🙋‍♂️ 어서와. 백엔드는 처음이지?</h2>
<p>네이버 부스트캠프 당시에 Node.js를 공부하기도 했었고, 인턴을 나름 풀스택 개발자 포지션으로 갔었기 때문에 Node.js로 백엔드 코드를 짜보기도 했다. 그래서 처음엔 Node.js를 사용해서 백엔드를 구현하려고 생각을 했었다. 하지만 이내 DB는 어떤 걸 사용해야 하고, 배포는 어떻게 해야 하는지 도저히 하루만에 끝낼 수 있을 것 같지는 않았다. 그러던 중 예전에 <strong>부트캠프에서 쇼핑몰 만들기 과제에서 사용했던 Firebase가 생각</strong>났다. 사실 당시에 제대로 공부해서 쓰지는 않았고, 강의에서 알려주는 내용들 바탕으로 열심히 따라했었기 때문에 정확히 어떻게 쓰면 되겠다는 생각보다는 <strong>&#39;Firebase로 굉장히 간편하게 백엔드를 구현할 수 있다&#39;</strong>는 느낌만 남아있었다. 무엇보다 Firebase를 사용하면 <strong>따로 배포를 할 필요가 없다</strong>는 게 가장 큰 장점이었다. 그리고 굉장히 가벼운 앱이고, 많아봐야 동시 접속자가 10명도 안 되기 때문에 성능적으로 크게 문제될 것 같지도 않았다.</p>
<p>프론트엔드의 경우는 첫 메인페이지가 <strong>질문 목록 페이지</strong>가 될 것이었기 때문에 <strong>이 부분을 서버 사이드 렌더링으로 구현해 보고 싶어서 Next.js를 선택</strong>했다. 물론, <strong>Vercel로 간편하게 배포할 수 있다</strong>는 점도 큰 장점으로 작용했다.</p>
<p>이렇게 <strong>&#39;딸깍 개발 기술스택&#39;</strong>이 완성되었다.</p>
<h2 id="🔥-firebase-realtime-database">🔥 Firebase Realtime Database</h2>
<p>이번에 Firebase를 써보면서 정말 편리하게 DB를 만들 수 있다는 것을 느꼈다. <strong>단 몇 번의 클릭만으로 DB 생성</strong>이 가능하다! 그리고 항상 <strong>배너에 안내가 잘 나와있었던 것 같아서</strong> 배너를 잘 보면서 만들었던 것 같다.</p>
<p>우선 로그인을 하고나서 우측 상단의 <strong>Go to console</strong>을 누르거나 구글에 <strong>Firebase Console로 검색</strong>해서 들어가면 아래 페이지가 나오고, <strong>프로젝트 만들기 버튼</strong>이 바로 있다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/fd6ef5c1-b2ce-4a53-95a9-e148945324f0/image.png" width="60%"/>
</p>

<p><strong>(1) 딸깍</strong></p>
<p>프로젝트 <strong>이름</strong>을 입력하고, <strong>AI 지원</strong>을 받을 것인지(설정하면 AI 채팅을 통해 Firebase 이용에 도움을 받을 수 있는 것 같다), <strong>구글 분석 서비스</strong>를 사용할 것인지 등을 설정한 후 프로젝트가 생성된다.</p>
<p>다음으로 해당 프로젝트에 들어가 보면</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/ca9abcd4-cf31-4af1-9d11-268a8da2cf7b/image.png" width="60%"/>
</p>

<p><strong>(2) 딸깍</strong></p>
<p>이렇게 배너에 <strong>앱에 Firebase를 추가</strong>하라는 안내가 뜬다. 내가 만들고 있는 앱은 웹 서비스였기 때문에 <strong>웹을 선택</strong>해줬다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/93b5b8a4-f22c-4a07-b4c3-d18ad51bb695/image.PNG" width="50%"/>
</p>

<p><strong>(3) 딸깍</strong></p>
<p><strong>앱 이름</strong>을 입력하고, <strong>호스팅 설정</strong>은 안 했다. 호스팅 설정을 하게 되면 Firebase에서 배포도 해주는 것 같지만, 배포는 Vercel로 하는 게 간편해 보여서 이 부분은 생략했다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/c6ab3728-d476-4552-a8dc-25a35c48fef2/image.png" width="60%"/>
</p>

<p><strong>(4) 딸깍</strong></p>
<p>다음으로 Firebase SDK 추가에 대한 안내가 나오고, <strong>개발중인 앱으로 가서 firebase를 설치</strong>하고, <strong>아래의 코드를 작성</strong>해주면 Firebase를 사용할 준비가 끝난다. 이 코드는 나중에 프로젝트 설정에 들어가서 다시 확인할 수 있다. <strong>회색으로 블러 처리된 부분</strong>은 <strong>환경 변수로 따로 설정</strong>해두고, 해당 환경 변수를 사용해주는 것이 좋다.</p>
<pre><code class="language-js">import { initializeApp } from &quot;firebase/app&quot;;

const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  databaseURL: process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
  measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
};

const app = initializeApp(firebaseConfig);</code></pre>
<p>그리고 나는 Realtime Database를 이용할 것이기 때문에</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/19fb975d-9fad-47d5-a01a-89cea9a805d8/image.png" width="50%"/>
</p>

<p><strong>(5) 딸깍</strong></p>
<p><strong>Realtime Database를 선택</strong>해서 들어갔고, </p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/00f56a33-3162-4f5c-9af7-ec17614e1239/image.png" width="60%"/>
</p>

<p><strong>(6) 딸깍</strong></p>
<p>들어가면 또 배너에 친절하게 <strong>데이터베이스 만들기</strong>라는 버튼이 있다. 클릭하면 데이터베이스 설정 모달이 뜬다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/c655587c-29fe-4a62-a8c3-f9df6fe1c533/image.PNG" width="60%"/>
</p>

<p><strong>(7) 딸깍</strong></p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/fba9b15d-b426-4fc2-a2c2-6a791ba3f4c8/image.PNG" width="60%"/>
</p>

<p><strong>(8) 딸깍</strong></p>
<p>데이터베이스 위치와 보안 규칙을 설정하게 된다. 아시아권은 싱가포르 밖에 없었고, 아무래도 가까우면 좋지 않을까 싶어서 <strong>싱가포르</strong>를 선택했다. 보안 규칙은 크게 중요하지 않았기 때문에 <strong>테스트 모드</strong>를 선택했다.</p>
<p>이렇게 하고 나면, 정말로 <strong>Firebase의 Realtime Database를 사용하기 위한 설정은 끝</strong>이 난다. 생략된 부분까지 포함해서 <strong>도합 20번 내외의 &#39;딸깍&#39;으로 설정이 완료</strong>가 됐다.</p>
<h2 id="📱✨-무엇보다-사용하기-편한-앱을-만들어-보자">📱✨ 무엇보다 사용하기 편한 앱을 만들어 보자!</h2>
<blockquote>
<p><em><strong>&quot;UX가 모든 사용자를 고려하지 않는다면, Some User Experience로 불려야 하지 않을까? 또는 SUX로?(Sucks와 동일한 발음)&quot;</strong> -Billy Gregory</em></p>
</blockquote>
<p>이 앱을 개발하면서 무엇보다 중요시 했던 것은 <strong>사용자의 편의성</strong>이었다. 우리가 <strong>노션에 질문리스트를 작성</strong>하고, <strong>GPT에게 랜덤으로 문제를 뽑아달라고 요청</strong>하고, 또 <strong>GPT에게 해당 문제에 스터디원을 적절하게 배정해달라고 요청</strong>하고, 면접을 진행하면서 <strong>이슈 사항을 노션에 기록</strong>하고, <strong>이미 진행한 질문들을 따로 분류</strong>하는 일련의 과정들을 <strong>보다 편하게 진행</strong>할 수 있어야 했다.</p>
<p>그래서 <strong>유저의 입력을 받아 진행하는 기능을 최소화</strong> 했고, 최대한 <strong>버튼 클릭만으로 대부분의 진행이 가능하도록</strong> 구현했다.</p>
<h3 id="면접-질문-목록-페이지-1">면접 질문 목록 페이지</h3>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/72b3c421-c3f5-45bd-a86e-8085f03cd006/image.PNG" width="70%"/>
</p>

<p>질문 목록 페이지는 굉장히 단순하게 구현했다. <strong>질문 리스트를 나열</strong>했고, <strong>셀렉션을 이용해서 원하는 카테고리의 질문만 볼 수 있도록</strong> 구현했다. 각각의 <strong>리스트는 완료 상태 체크 표시가 가능</strong>하고 체크를 표시하거나 해제할 때마다 즉시 DB 데이터도 업데이트 되도록 구현했다. 또한 <strong>질문의 추가, 삭제가 가능</strong>하도록 구현했다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/5a256aa3-7d82-417e-ad05-309db622ce9d/image.PNG" width="60%"/>
</p>

<p>질문 추가 모달의 경우 각각의 <strong>질문을 줄바꿈으로 구분하여 여러 개를 동시에 입력할 수도 있게</strong> 구현했다.</p>
<h3 id="면접-진행-페이지-1">면접 진행 페이지</h3>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/6fbd7a75-669e-4094-ba16-9a19f153aec3/image.PNG" width="60%"/>
</p>

<p>그리고 <strong>면접 시작 버튼</strong>을 누르면, <strong>면접 진행 페이지로 이동</strong>하게 되는데, 면접 질문 목록에서 <strong>체크 표시가 되지 않은 질문들 중에 기본 질문 1개와 기술 질문 5개를 랜덤으로 뽑아준다.</strong> 이렇게 질문을 뽑아줌과 동시에 이 면접은 <strong>면접 목록에 자동으로 저장</strong>된다. 이렇게 생성된 면접들은 우측 상단에 <strong>마이페이지 아이콘을 클릭</strong>으로 연결되는 <strong>면접 목록 페이지</strong>에서 확인할 수 있다. 다음 단계로 넘어가기 위해서는 <strong>면접자 입력하기 버튼</strong>을 누르면 된다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/3b109f88-e7e3-42ec-8f3e-94edad11aaf8/image.PNG" width="60%"/>
</p>

<p>이렇게 한 명씩 입력할 수 있고, 일일이 면접자 추가 버튼을 누르게 하는 것도 불편할 것 같아서 <strong>엔터키로 입력</strong>할 수 있도록 구현했다. 입력된 면접자들로 면접을 진행하기 위해서는 <strong>면접 진행하기 버튼</strong>을 누르면 된다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/7794144c-e039-429f-9362-95d003c622bd/image.PNG" width="60%"/>
</p>

<p>이런식으로 각각의 질문마다 배정된 면접자를 보여준다. <strong>기본 질문의 경우</strong> 모두가 답하기 때문에 <strong>면접자 전원이 배정</strong>되고, <strong>기술 질문의 경우 한 질문당 2명씩 배정</strong>이 된다. 또한 해당 질문에 대한 모의 면접을 진행하던 중 나온 꼬리 질문이나 추가적인 토론 내용, 그리고 참고할만한 내용 같은 것들을 <strong>댓글 형태로 남길 수 있도록 구현</strong>했다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/641439be-5605-4f04-900d-e164d1891ef7/image.PNG" width="60%"/>
</p>


<p>마지막 질문에서는 다음 질문 버튼 대신 면접 완료 버튼이 있고, <strong>면접 완료 버튼</strong>을 누르면 면접을 종료할 수 있다.</p>
<h3 id="면접-목록-페이지-1">면접 목록 페이지</h3>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/9a8f77d5-86b1-490c-9673-7cc89e4fef34/image.PNG" width="70%"/>
</p>

<p>각각의 면접들은 면접질문을 고르는 순간 자동으로 면접 목록에 저장이 된다. <strong>면접을 저장할 때 제목 등을 입력하는 것도 굉장히 불편</strong>하다는 느낌이 들어서 <strong>자동으로 저장</strong>되도록 했다. 제목을 따로 입력받지 않기 때문에 <strong>타임스탬프를 이용해서 제목</strong>을 만들어 주고, 해당 면접에 포함된 <strong>질문의 내용을 일부 확인할 수 있도록</strong> 구현했다.</p>
<p>완료된 면접의 경우 면접 목록에서 <strong>완료 뱃지로 표시</strong>되며, 아직 <strong>완료되지 않은 면접은 진행중 뱃지로 표시</strong>된다. 완료된 면접도 해당 카드를 클릭해서 진행되었던 내용을 다시금 확인할 수 있다. 또한 면접 완료시에 <strong>완료된 면접에 포함되어 있던 질문들은 자동으로 완료 상태로 체크가 되도록 구현</strong>해두었기 때문에 다음 질문 생성 때는 포함되지 않는다.</p>
<h2 id="🌱-나의-첫-풀스택-사이드-프로젝트-소감">🌱 나의 첫 풀스택 사이드 프로젝트 소감!</h2>
<p>단순히 앱의 목적 자체가 스터디원들끼리 같이 면접을 진행하고 진행 내용을 기록하는 용도였기 때문에 딱히 로그인 기능이 필요하지도 않았고, 단순한 CRUD를 구현하는 것이 전부인 앱이었다. 하지만, 그럼에도 마냥 순탄하지만은 않았던 것 같다. 개발을 진행하며 겪었던 몇 가지 이슈를 공유해보고자 한다.</p>
<h3 id="낯선-서비스-사용은-언제나-어렵다">낯선 서비스 사용은 언제나 어렵다</h3>
<p>위에는 마치 Firebase 홈페이지를 들어가면 누구나 간단히 프로젝트를 진행할 수 있을 것 처럼 써놓긴 했지만, <strong>나름의 시행착오</strong>를 많이 겪었다. 처음 Firebase 홈페이지를 들어갔을 땐 뭐부터 해야할지 난감하기도 했고, Storage를 써야 되는건지, Realtime Database를 써야 되는건지, 호스팅은 뭐고, Authentication은 필수인건지... 등등 굉장히 많이 헤맸다. 하지만 늘 느끼는 것인데, 헤맬 때 블로그나 유튜브를 참고하는 것도 좋지만 가장 정확한 정보는 공식문서에 있다. Firebase도 마찬가지로 <a href="https://firebase.google.com/docs?hl=ko">Firebase 공식문서</a>에 들어가보면 <strong>Firebase 기초 사용법에 대한 안내</strong>가 굉장히 자세하게 나와있고, 이번에 느낀거지만 <strong>docs 문서임에도 번역이 굉장히 깔끔하고 폰트나 텍스트 크기 등 디자인 자체가 보기 어렵지 않게 잘 정리</strong>되어 있었다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/e256099a-d08f-4f3f-9535-70814256ec56/image.PNG" width="70%"/>
</p>

<h3 id="ai가-모든-것을-다-해주지는-않는다">AI가 모든 것을 다 해주지는 않는다!</h3>
<p><strong>GPT와 Claude의 도움</strong>이 없었다면 이 모든 것을 하루만에 구현하기란 어려웠을 것이다. 물론 위의 저 투박한 버튼을 만들어준 것도 이녀석들이다.(<del>제가 안 그랬어요. 얘네가 그랬어요</del>) 구체적으로 어떤 모양의 아이콘을 지정해주지 않았더니 저런식으로 만들어줬다. 아직까지 <strong>알잘딱깔센의 수준에는 못 미치는듯 하다.</strong> 그래도 구체적으로 어떤 기능을 가진 어떤 형태의 UI를 구현한다고 했을 때 <strong>처음 답변으로 던져주는 코드의 퀄리티가 1년 전쯤에 비해서 많이 좋아진 것 같다</strong>고 느꼈다. 대부분 동작하는데 큰 문제는 없었고 정말 이상한 부분만 고쳤던 것 같다.</p>
<p>문제는 면접 진행 단계에서 질문마다 면접자를 배정하는 로직에 있었다. <strong>면접 질문은 총 6개, 그 중 1개의 기본 질문에는 모든 면접자가 배정되고, 나머지 5개의 기술 질문에 면접자들이 고르게 배정되어야 했다.</strong> <strong>&#39;고르게&#39;</strong>라는 의미는 <strong>각 질문당 배정받은 면접자 수도 균일하면서 각 면접자당 배정된 질문수도 균일</strong>해야 했다. 딱히 떠오르는 효과적인 알고리즘이 없었기 때문에 AI에게 전적으로 부탁했다. 여기서 문제가 터지기 시작했다. 처음에 답변으로 받았던 로직이 너무 복잡했고, 로직을 이해하기도 어려웠다. 그래서 코드를 수정하는 것을 포기하고 <strong>동작을 확인 해보고 안 되는 부분을 다시 AI에게 맡기기를 반복</strong>했다. 그렇게 이해하기를 포기하고 AI의 톱니바퀴처럼 복사, 붙여넣기만 하던 나에게 경종을 울린 것은 유튜브였다. 유튜브에 BGM처럼 틀어놨던 다섯시간짜리 브레이킹베드 전시즌 몰아보기가 끝이 났다. </p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/3427ed90-66cb-40fd-86fa-837234e0585f/image.jpeg" width="50%"/>
</p>


<p>그렇게 정신을 다시 가다듬고, AI가 작성해 준 로직을 깔끔하게 지워버렸다. AI가 명확하게 처리를 못 했던 이유는 아마도 <strong>&#39;면접자에게 2개의 기술 질문을 배정할지&#39;</strong>, <strong>&#39;기술 질문에 2명의 면접자를 배정할지&#39;</strong>가 <strong>모호했기 때문</strong>인 것 같다. 질문을 던지는 나로서도 어떻게 해야할지 결정을 못 내린 상태였기 때문에 AI는 이 두 가지 방식이 혼재하는 코드를 작성해줬다. 그리고 또 다른 이유는 AI가 <strong>최대한 효율적인 코드를 짜려고 했던 것</strong> 같다. 사실 면접자는 많아봐야 10명도 안될 것이다. 질문도 6개가 끝이고, 진행방식을 바꾼다고 하더라도 10개를 넘지 않을 것이다. 그렇기 때문에 <strong>알고리즘 효율이 크게 중요하지 않은 문제</strong>임에도 AI는 항상 숫자가 큰 경우까지 고려하려고 했던 것 같다.</p>
<p>우선 면접 진행을 질문 순서대로 진행할 것이기 때문에 데이터 구조에서 questions 데이터에 interviewees 배열을 가지고 있는 것이 좀 더 구현에 유리할 것으로 생각해서 <strong>질문에 면접자를 배정하는 방식을 채택</strong>했다. 그리고 로직은 단순하게 <strong>백트래킹을 이용</strong>해서 전체 면접자 중에서 <strong>2명을 뽑을 수 있는 조합을 전부 탐색해서 2명 조합 배열을 만들었다.</strong> 사실 백트래킹으로 배열을 만들게 되면 순서를 연속적으로 탐색하게 되기 때문에 결국 배정되는 방식도 면접자 순서대로 배정하게 돼서 면접자 입력 순서에 따라 질문 예측이 가능해지는 문제가 있다. 따라서 <strong>배정하기 전에 2명 조합 배열 자체를 섞어주는 셔플 함수를 만들어 랜덤성을 추가</strong>했다. 여기까지 하면 <strong>질문당 2명씩 배정하는 문제는 해결이 됐지만 각각의 면접자가 고르게 질문을 배정받는다는 보장은 없다.</strong> 그래서 이 부분을 해결하기 위해 2명을 배정하기 전에 <strong>2명의 조합 배열에서 2명이 이전까지 배정 받은 문제 수의 합이 최솟값인 조합만 필터링 해서 걸러내는</strong> 작업을 추가했다. 이렇게 면접자 배정 로직이 완성되었다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/be5502b8-7ec5-406a-8ff6-92c08c507c4b/image.jpg" width="60%"/>
</p>

<p>내가 이깄다... 내가 이깄쓰!!</p>
<h3 id="진짜-필요에-의해-만든-서비스">진짜 필요에 의해 만든 서비스!!</h3>
<p>이 앱은 포트폴리오에 추가할 것도 아니고, 정말 면접 스터디만을 위해 만든, <strong>내가 진짜 사용하기 위해 만든 첫 서비스</strong>이다! 그 부분이 감회가 굉장히 색다르고, 프론트엔드부터 백엔드까지 혼자서(AI의 도움은 받았지만) 개발하고 배포까지 했다는 점에서 굉장히 뿌듯한 프로젝트다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[파란만장 비전공자 개발자의 구름톤 in JEJU 10기 대상 후기! <3편> - 完]]></title>
            <link>https://velog.io/@jiiker_/%ED%8C%8C%EB%9E%80%EB%A7%8C%EC%9E%A5-%EB%B9%84%EC%A0%84%EA%B3%B5-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-%EA%B5%AC%EB%A6%84%ED%86%A4-in-JEJU-10%EA%B8%B0-%EB%8C%80%EC%83%81-%ED%9B%84%EA%B8%B0-3%ED%8E%B8-%E5%AE%8C</link>
            <guid>https://velog.io/@jiiker_/%ED%8C%8C%EB%9E%80%EB%A7%8C%EC%9E%A5-%EB%B9%84%EC%A0%84%EA%B3%B5-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-%EA%B5%AC%EB%A6%84%ED%86%A4-in-JEJU-10%EA%B8%B0-%EB%8C%80%EC%83%81-%ED%9B%84%EA%B8%B0-3%ED%8E%B8-%E5%AE%8C</guid>
            <pubDate>Mon, 04 Nov 2024 04:34:12 GMT</pubDate>
            <description><![CDATA[<p>3일차 저녁에 기분전환겸 성산일출봉을 보러 나갔을 때 찍었던 사진이다. 뭔가 우리의 해커톤 당시의 모습을 잘 요약해주는 느낌이 들어서 우리가 찍었던 사진들 중에 내가 제일 좋아하는 사진이다. <strong>지들만 아는 얘기 하면서 셋이 모여가지고 트러블 슈팅 하는 개발자들, 개발 이슈와 맞물려 작업하긴 하지만 결국 혼자 해야하는 고독한 디자이너, 한 걸음 떨어져서 모든 걸 지켜보고 있는 기획자...</strong> 거기에 성산일출봉 뷰까지... 이건 정말 올해의 퓰리처상 감이다.</p>
<h2 id="3일차-9am">3일차 9AM</h2>
<blockquote>
<p><em><strong>&quot;지금 바꾸는 거 아닌 게 어디야~&quot;</strong></em></p>
</blockquote>
<p>우리 팀의 <strong>긍정적이고 강한 멘탈</strong>을 단적으로 보여주는 문장이었다. 특히, 우리가 11시 언저리부터 새벽 2시까지 주제를 바꿀지 말지 논의할 때, 혼자서 묵묵히 혹시라도 하게될 수도 있는 &#39;제주 장바구니&#39;의 디자인 작업을 하고 있었던 디자이너 J양. 디자인 초안이 거의 완성된 상태였지만, 아이디어를 변경하기로 결정하자마자 쿨하게 피그마 페이지를 바꿔서 새롭게 작업하는 모습이 마치 만화 원피스에서 말썽쟁이 루피의 곁을 오랫동안 지켜온 든든한 오른팔, 롤로노아 조로를 보는 것 같았다. 그렇게 <strong>주제를 &#39;뿌리&#39;로 결정</strong>하고 난 뒤, 일단 잠을 좀 자고 아침 9시에 다시 모이기로 했다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/5d04c6a7-7ef9-4375-9c6a-5df17296576e/image.png" width="70%"/>
</p>

<h3 id="진짜-시작">진짜 시작!</h3>
<p>대략 3시간 정도 자고 9시에 다시 모였다. 그래도 자기 전에 아이디어가 정해졌다는 게 정말 큰 안심이 되었다. 다만, 원래 아이디어로 진행했다면, 어제 어느 정도 디자인 작업을 해서 그걸 바탕으로 프론트 작업을 시작했겠지만, 아이디어를 변경 하고 바로 자러 갔기 때문에 디자인 작업을 새롭게 시작해야 했다. 그렇다고 마냥 디자인이 완성될 때까지 기다리기엔 <strong>1분 1초가 아쉬웠기 때문에, 프론트 개발도 바로 시작</strong>했다. </p>
<p>디자이너와 같이 프로젝트를 한 것은 처음이었기 때문에 초반에는 실수도 있었지만, 빠르게 적응했던 것 같다. 부트캠프 과정에서 진행했던 프로젝트들은 디자이너가 없었기 때문에 프론트 개발자가 피그마 시안을 작성했었다. 그러다보니 해당 시안은 언제든지 바뀔 수 있는 것이었고, &#39;이런 느낌으로 개발할 거에요~&#39; 같은 참고 사항이었다. 하지만, <strong>디자이너의 피그마 시안은 1px 단위까지 절대적으로 지켜야하는 것</strong>이었다. </p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/86c4281a-91c4-462e-9858-59d47520d665/image.jfif" width="50%"/>
</p>

<p>책 표지가 너무 재밌어서 언젠가 한 번 읽어봐야지 했던 책인데, 저 말을 진짜로 듣게 될 줄은 몰랐다. 디자인 작업이 완료되지 않은 상태에서 프론트 작업을 병행하다 보니 어쩔 수 없이 디자인 변경 사항을 반영할 일이 잦았던 것 같다.</p>
<p>그래도 디자이너와 함께 작업하면서 <strong>왜 웹 디자이너와 프론트엔드 개발자가 나뉘어 있는지 이해</strong>할 수 있었다. 이전의 프로젝트에서 프론트 개발자끼리 디자인에 대해 견해 차이가 있을 경우에는 정말 답을 명확히 내릴 수 없는 문제에 대해 끝 없는 논쟁을 해야 했었다. 이런 <strong>쓸데없는 에너지 소모를 줄일 수 있는 것</strong>만 해도 너무 편했고, 무엇보다도 굉장히 <strong>고퀄리티의 디자인을 통해 작업의 완성도를 한층 높일 수 있었다.</strong></p>
<h3 id="팀-워크">팀 워크</h3>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/1e5099d9-2976-4dab-8486-b49960d84496/image.PNG" width="70%"/>
</p>

<p>누군가가 역할을 정해준 것은 아니었지만, <strong>각자 자리에서 최선을 다했기에 자연스럽게 팀 워크가 형성되었다고 생각</strong>한다. 사실 내 경우에는 다른 프론트 개발자 J군이 나보다 훨씬 뛰어난 실력을 가지고 있었기 때문에 개발적인 부분에 있어서는 아무래도 그 친구가 나보다 한 일이 훨씬 많았다. 하지만, <strong>이 팀에서 내가 도움이 될 수 있는 부분을 계속해서 찾으려고 했다.</strong></p>
<p>나는 스스로 생각했을 때 <strong>어려운 것도 쉽게 풀어서 설명하는 능력이 뛰어나다</strong>고 생각했다. 그래서 백엔드 개발자 L군과 프론트 개발자 J군이 주로 개발적으로 가능한지 여부를 따져 판단하는 역할을 했고, 나는 <strong>정해진 사항의 기술적인 부분들을 풀어서 기획자에게 설명하고, 기획 의도를 구체화</strong>하는 데 힘을 보탰다.</p>
<h3 id="낭만-한-스푼">낭만 한 스푼</h3>
<blockquote>
<p><strong>_&quot;정상에 오른다는 목표를 항상 유념해야 한다. 하지만 산을 오르는 동안 펼쳐지는 무수한 볼거리 앞에서 이따금 멈춰 선다고 큰일이 날 것 까진 없다.&quot; _</strong> - 파울로 코엘료</p>
</blockquote>
<p>이 해커톤에 지원했던 이유가 <strong>성산일출봉이 보이는 데서 코딩하는 낭만 넘치는 해커톤</strong>이었기 때문이다. 여기서 코딩만 하고 가면 다른 해커톤과 다를 게 없지 않겠는가? 다 같이 머리도 식힐겸 <strong>제주 밤바다 구경</strong>을 나섰다. </p>
<p>플레이스캠프를 나서면서 외부 조명장식이 너무 예뻐서 여기서도 한 컷을 남겼다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/28174199-027c-43b9-ad6d-0fbec668041d/image.jpg" width="50%"/>
</p>

<p>플레이스캠프를 나와서 한 10분정도 길을 따라 걷다보면,</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/374f384f-1b00-4fa1-a0cc-12da11ce9cbe/image.jpg" width="50%"/>
</p>

<p>성산일출봉이 보이는 해안에 도착할 수 있다.</p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/38b5e5f4-1b60-4725-9ca3-044a985fe01b/image.jpg" width="70%"/>
</p>

<p>그리고 이 때, 내가 제일 좋아하는 썸네일의 사진도 찍었다!</p>
<p>어느 정도 낭만을 즐기고 나서, <strong>재충전된 마음으로 다시 플레이스캠프에 돌아왔다.</strong></p>
<h2 id="4일차-11am">4일차 11AM</h2>
<p>오전 11시까지 제출이었기 때문에 11시까지는 무조건 달리고, 다 끝내고 잔다는 생각으로 임했다.(나는 중간에 너무 피곤해서 20분 정도 엎드려서 잤던 것 같다.) 보통은 디자이너와 기획자가 1-2일차에 고생하고, 뒤에는 조금 편하게 참여하는 것으로 알고 있었는데, 우리 팀은 <strong>모두가 마지막까지 긴장의 끈을 놓을 수가 없었다.</strong></p>
<p>아무래도 <strong>개선된 UI/UX가 우리 서비스의 핵심</strong>이다보니, <strong>디자이너</strong>는 어떤 디자인이 <strong>더 나은 UX를 제공해줄 수 있을지 끊임없이 고민</strong>했고, <strong>기획자</strong> 또한 발표에 있어서 어떤 임팩트 있는 기능 시연을 보여줄 수 있는 것이 아니다 보니 <strong>말로써 기획 의도를 잘 전달해야 한다는 부담</strong>을 가지고 있었던 것 같다.</p>
<p>그렇게 <strong>모두가 함께 달려서 11시 제출을 완료</strong>했다.</p>
<h2 id="해커톤-발표-및-결과-발표">해커톤 발표 및 결과 발표</h2>
<blockquote>
<p><em><strong>진인사대천명(盡人事待天命)</strong></em>
<em>인간으로서 해야 할 일을 다 하고 나서 하늘의 명을 기다린다.</em></p>
</blockquote>
<p>다른 팀들의 발표를 들으면서 정말 이걸 시간내에 어떻게 다 만들었지 싶은 팀도 있었고, 정말 제주도 느낌이 물씬 나는 예쁜 디자인의 서비스를 만든 팀도 있었다. 모든 팀들이 정말 열정적으로 참여했다는 것을 발표를 통해 잘 느낄 수 있었다.</p>
<p>그렇게 우리 차례가 왔다. 우리팀 기획자 H양이 발표에 대해 걱정을 많이 했었는데, 그 <strong>걱정들이 무색하리만치 훌륭한 발표</strong>였다. 특히, <strong>심사위원들의 기술 질문</strong>에 대해서 개발자들에게 마이크를 넘겨도 되는 부분이었지만, 발표 준비를 하면서 우리와 얘기했던 것들을 토대로 <strong>혼자서 대답했던 게 인상적</strong>이었다. 우리 팀 서비스에 대한 평가는 전반적으로 <strong>&quot;와우 포인트는 없지만, 지금 제주도에 정말 필요한 서비스&quot;</strong>라는 평을 받았다.</p>
<p>그렇게 발표를 마치고, 기다림의 시간이 찾아왔다. 기다리는 동안 우리 팀의 개발 과정에 대해 궁금해 하시는 심사위원분이 계셨다. 우리 팀 서비스 내에 AI를 통해 데이터를 가공하는 과정이 있었는데, 이 부분에서 AI를 어떻게 사용했는지 궁금해 하셨고, 우리 팀이 사용했던 AI 프롬프트와 데이터를 가공하면서 겪었던 트러블 슈팅에 대해 말씀드렸다. 그 부분에 대해서 재밌어 하시면서 AI를 사용하는 방법에 대해 몇 가지 조언도 해주셨다.</p>
<h3 id="대망의-결과">대망의 결과</h3>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/6bfd65aa-88e2-4acb-808c-c59022b3ae3b/image.jpg" width="60%"/>
</p>

<p><strong>대상이라니...</strong> 믿기지 않았다. 워낙에 발표에서 좋은 평가를 해주셔서 수상은 할 수 있을지도 모르겠다는 생각을 했지만, 대상을 받을 거라고는 생각하지 못 했다. 우수상, 최우수상에도 우리 팀이 나오지 않아서 혹시나 하는 마음에 다 같이 손을 잡고 기다리다가 대상 발표에 너무 놀라서 소리를 질러버렸다... 잠을 못 자서 피곤하던 것도 감쪽같이 사라졌고, 공항으로 가는 버스 안에서도 들떠서 잠을 이루지 못 했다. 1년치 도파민을 한꺼번에 받으면 이런 느낌일까... 참여에 의의를 뒀던 해커톤이었지만, 결과까지 좋아서 너무나 만족스러웠다.</p>
<p>심사위원분께서 말씀하셨던 내용 중에 그런 게 있었다. 해커톤을 하다보면 많은 팀들이 <strong>&#39;특별한 것&#39;을 만드는 것에만 집중하다가 결국 아무도 쓰지 않을 &#39;이상한 것&#39;을 만드는 경우</strong>를 많이 보게 된다고 한다. 그런 면에서 봤을 때 우리 팀의 <strong>뿌리 서비스는 특별한 점은 없었지만 정말 필요하고 실질적인 도움을 줄 수 있는 서비스</strong>였기 때문에 높은 점수를 받지 않았을까 싶다.</p>
<p>평가들을 종합해 봤을 때, <strong>제주도의 청년 인구 감소에 대한 정확한 문제 인식</strong>과 그 <strong>문제를 해결하기 위한 서비스로서 뿌리 서비스의 타당성</strong>. 그리고 해커톤이니만큼 <strong>문제 해결 과정에서의 팀원들의 역할 분담</strong> 또한 중요한 요소였을 것으로 생각된다.</p>
<h2 id="소감">소감</h2>
<p>구름톤은 <strong>디자이너, 기획자와 같이 팀을 이루어 프로젝트를 진행해 볼 수 있는 정말 좋은 기회</strong>이다. 대상이라는 좋은 결과가 있긴 했지만, 그보다 더 의미있는 건 해커톤 기간동안 <strong>함께 고생하고 이겨낸 동료들을 얻었다는 점</strong>이다. 해커톤 이후에도 종종 모여서 술을 마시는데, 누가 밤새 해커톤 한 사이 아니랄까봐 술도 해뜰때까지 마시는 굉장히 전우애 넘치는 동료들을 얻었다. 농담이고(<del>농담 아님</del>), 나 같은 비전공자 출신의 개발자는 주변에 개발 관련자가 거의 없기 때문에, 부트캠프나 해커톤 등에서 만나는 <strong>열정적인 동료들이 너무나 소중한 인맥</strong>이 된다. 수상을 하지 않았더라도 정말 좋은 경험이 되었을 것임은 분명하다.</p>
<p>벌써 5개월이나 지나서 감흥이 다 사라졌다고 생각했는데, 글을 쓰다보니 당시의 일들이 너무나도 생생하게 기억이 난다. 아무래도 오늘밤엔 잠을 설칠 것 같다. </p>
<p>구름톤 10기 함께 하셨던 모든 분들 고생 많으셨습니다! 
또한, 저번주에 끝났을 구름톤 11기에 참여하셨던 분들도 고생 많으셨습니다!</p>
<blockquote>
<p>저희 팀이 만들었던 <strong>뿌리 서비스</strong>에 대한 자세한 설명은 <a href="https://9oormthon.goorm.io/83182701-089a-4441-8589-c072b4102fe6">구름톤 전시관</a>에서 확인하실 수 있습니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[우당탕탕 비전공자 개발자의 구름톤 in JEJU 10기 대상 후기! <2편>]]></title>
            <link>https://velog.io/@jiiker_/%EC%9A%B0%EB%8B%B9%ED%83%95%ED%83%95-%EB%B9%84%EC%A0%84%EA%B3%B5-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-%EA%B5%AC%EB%A6%84%ED%86%A4-in-JEJU-10%EA%B8%B0-%EB%8C%80%EC%83%81-%ED%9B%84%EA%B8%B0-2%ED%8E%B8</link>
            <guid>https://velog.io/@jiiker_/%EC%9A%B0%EB%8B%B9%ED%83%95%ED%83%95-%EB%B9%84%EC%A0%84%EA%B3%B5-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-%EA%B5%AC%EB%A6%84%ED%86%A4-in-JEJU-10%EA%B8%B0-%EB%8C%80%EC%83%81-%ED%9B%84%EA%B8%B0-2%ED%8E%B8</guid>
            <pubDate>Fri, 01 Nov 2024 01:35:43 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>저희 팀이 만들었던 <strong>뿌리 서비스</strong>에 대한 자세한 설명은 <a href="https://9oormthon.goorm.io/83182701-089a-4441-8589-c072b4102fe6">구름톤 전시관</a>에서 확인하실 수 있습니다.</p>
</blockquote>
<p>구름톤 10기부터는 참가자 전원이 아이디어톤에 참여해야 했기 때문에 1일차 일정이 끝나고 난 뒤에 아이디어톤 발표자료를 준비해야 했다. 첫 날 교육 장소에 들어올 때 뽑은 번호표를 가지고 조를 정했었는데, 그 조원들과 함께 저녁식사를 하고, 카페에서 아이디어톤 준비를 했다. 나는 <a href="https://velog.io/@jiiker_/%EB%8D%94-%EB%8A%A6%EA%B8%B0-%EC%A0%84%EC%97%90-%EC%8D%A8%EB%B3%B4%EB%8A%94-%EB%B9%84%EC%A0%84%EA%B3%B5-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-%EA%B5%AC%EB%A6%84%ED%86%A4-in-JEJU-10%EA%B8%B0-%EB%8C%80%EC%83%81-%ED%9B%84%EA%B8%B0">&lt;1편&gt;</a>에서 얘기했던 <strong>&#39;걸어서 땅따먹기&#39;</strong> 서비스를 가지고 발표 준비를 했고, 아무래도 첫 날부터 늦게 자면 안될 것 같아서 자료의 퀄리티에는 크게 신경쓰지 않았다.(<del>앞서 첨부했던 이미지가 실제 발표자료입니다. 허헣</del>) 다른 팀 중에는 포지션이 골고루 섞여있어서 첫 날부터 내일 팀을 어느정도 구성하는 조도 있었던 것 같다. 하지만 우리 조 구성은 백엔드 한 명과 프론트 다섯 명이었기 때문에 서로 흩어질 운명을 직감해서일까... 내일 팀에 대해서는 서로 얘기하지 않았다.</p>
<h2 id="2일차-아침">2일차 아침</h2>
<p>2일차 오전 교육 장소가 제주시인 점을 고려해 첫 날 조원 분께서 알려주신 게스트 하우스에서 같이 묵었다. 게스트 하우스에서 나름 조식으로 제공해주는 시리얼을 먹고서 어제 조원 분들과 만나 택시를 타고서 교육장소로 이동했다. 1일차까지는 그래도 뭔가 제주도에 여행 온 것 같은 가벼운 마음이었는데, <strong>2일차 아침부터는 살짝 긴장감이 맴돌았던 것 같다.</strong> 조금 일찍 도착해서 카카오 돌하르방에게 코딩도 좀 가르쳐 주고(?), 학교 앞에 있어서 흔한 카페인 줄 알았지만 사실은 제주가 본점이었던 에이바우트커피에서 커피도 한 잔 사와서 교육 준비를 마쳤다. </p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/08e45195-2715-4253-9136-3f92010b938f/image.jpg" width="40%"/>
</p>

<h3 id="1분은-너무-짧고-3분은-너무-길다">1분은 너무 짧고, 3분은 너무 길다!</h3>
<p>오전 교육 세션이 끝나고, 이어서 2분 아이디어톤이 시작되었다. 정말 다양한 사람들의 다양한 발표를 들을 수 있었다. 기억에 남는 발표는 굉장히 발성이 좋으셨던 기획자 분의 발표였다. 뭔가 대기업 에이스 신입사원의 발표를 보는듯한(?) 느낌이 들었다. 2분이지만 굉장히 여유 넘치게 발표하시는 분도 계셨고, 아이디어는 대충 발표하고 본인의 역량을 어필하시는 분도 계셨다.</p>
<p>다른 분들의 아이디어를 다 들어봤지만, 나는 역시 &#39;걸어서 땅따먹기&#39; 서비스를 개발해야겠다고 마음먹었다. 그렇게 아이디어톤이 끝나자마자 눈여겨 봐뒀던 분들 쪽으로 고개를 돌렸는데, 웬걸, 이미 어느 정도 무리가 형성되어 있었다. 다른 대안을 마련해 두지 않았기 때문에, <strong>사실 이 때 &#39;난 망했다&#39;고 생각했다.</strong> 그런데 갑자기 누군가 내 쪽으로 걸어왔다. 그가 내 이름을 불러주었을 때, 나는 그에게로 가서 꽃이 되었다.</p>
<blockquote>
<p><em><strong>&quot;프론트 개발자시죠?&quot; &quot;기술 스택 뭐 쓰세요?&quot;</strong></em></p>
</blockquote>
<h3 id="쓰라는-것-다-쓰겠습니다-형님">쓰라는 것 다 쓰겠습니다. 형님.</h3>
<p>구름톤에 참여하기 전까지 리팩토링 하던 프로젝트에서 <strong>Next.js</strong>와 <strong>TypeScript</strong>를 사용했었기 때문에, 그나마 제일 익숙한 Next.js와 TypeScript를 얘기했는데, 마침 기술 스택이 잘 맞는다며 같이 하자고 했다. 나는 흔쾌히 승낙했고, <strong>나를 마지막 퍼즐로 팀이 완성되었다.</strong></p>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/d575dd08-0ce5-4623-ab3c-25de9f935046/image.jpg" width="40%"/>
<figcaption style="text-align:center; font-size:15px; color:#808080;">
한 팀에 기획자1, 디자이너1, 프론트2, 백1
</figcaption>
</p>

<p>팀은 프론트 개발자를 제외하고 한 명씩으로 구성되어 있었다. 프론트는 두 명이었기 때문에 서로 의지할 수 있었지만, 다른 포지션은 온전히 혼자서 해야 했기에 부담감이 조금은 더 있었을 것 같다. 백엔드 개발자 분들이 보통 배포까지 담당 했었는데, 처음 사용해 보는 <strong>크램폴린 IDE</strong> 때문에 특히나 부담이 됐을 것 같다. 그래서 그런지 백엔드 분들은 조 구분 없이 서로 도움을 주고 받는 모습이 많이 보였다.</p>
<h2 id="순조로운-출발">순조로운 출발</h2>
<p>팀이 구성되고 나서 각자 발표했던 아이디어를 다시 한 번 설명하고, 그 아이디어들 중에서 하나를 고르기로 했다. 내가 <strong>아이디어를 선택했던 기준</strong>은 크게 3가지 정도였던 것 같다.</p>
<blockquote>
<p><em><strong>1. 주제에 얼마나 부합하는지
2. 나라면 해당 서비스를 실제로 쓸 것 같은지
3. 기술적으로 도전 포인트가 있는지</strong></em></p>
</blockquote>
<p>우리 팀원들이 정말 대단했던 게 다들 어제 처음 만난 사람이라는 게 믿기지 않을 정도로 소통이 원활했다.(<del>플래그 금지</del>) 특정 의견에 대해 논의할 때 빠른 피드백과 그 피드백을 수용하려는 자세가 돋보였던 것 같다. 그래서 모든 것들이 굉장히 빠르게 결정되었다.(<del>멈춰...</del>) 숙소로 이동하는 버스에 오르기 전에 이미 아이디어가 정해졌고, 어떤 방식으로 구현할지도 대강 구상이 끝난 상태로 이동했다.(<del>그만해...</del>) 그렇게 정해진 우리의 <strong>처음</strong> 아이디어는 가성비 제주여행 계획 서비스인 <strong>&#39;제주 장바구니&#39;</strong>였다.</p>
<h3 id="마셔라-마셔">마셔라 마셔!</h3>
<p>모든 게 순조로웠기에 굉장히 편안한 마음으로 비어파티에 참여했다. 오늘은 마시고! 내일 밤새서 만들면 돼!! 그러던 우리 조에 먹구름이 드리운 것은 비어파티가 끝나갈 때 쯤이었다. 백엔드 개발자 L군이 데이터 가공이 가능한지 테스트를 해봐야 겠다며 먼저 자리를 비웠다. 그리고 비어파티가 끝나고, L군이 먼저 가 있는 라운지로 다들 모였다. 처음엔 아이디어 구체화를 위한 자리였지만, 얘기가 진행됨에 따라 문제점이 하나둘씩 생겼다.</p>
<p>우선 <strong>&#39;가성비가 좋다&#39;는 것을 어떻게 판단해야할 지</strong>가 문제였다. 여행을 위해 소비하는 것들 중에는 호텔이 될 수도 있고, 쇼핑이 될 수도 있고, 음식이 될 수도 있다. 다양한 카테고리의 데이터들을 어떻게 분류할 것이며 카테고리 마다의 기준을 일일이 정하는 것은 너무나 힘든 작업이 될 것 같았다. 그래서 우선 <strong>음식 카테고리 하나만</strong>을 가지고 프로토타입을 만들어 보기로 했다.</p>
<p>다음으로 우리는 지도 매핑도 필요했기 때문에, 카카오 지도 API를 활용해서 구현하려고 했다. 그런데 카카오 지도 API를 통해 음식점 데이터를 받아도 해당 <strong>음식점의 메뉴까지 조회할 수는 없었다.</strong> 그래서 이 부분은 일단 데이터 처리에 시간이 좀 걸리더라도 <strong>음식점을 조회 한 뒤에 해당 음식점의 메뉴를 크롤링 해서 가져오는 방식</strong>으로 해보자고 결론이 났다.</p>
<p>그런데 이 문제들보다 더 큰 문제가 남아있었다. <strong>식당마다 가격 표기가 제각각인 문제</strong>가 있었다. 식당마다 1인분의 가격이 다르고, 1인분의 그람수가 다르고, 이런 모든 것들을 고려해서 데이터를 가공하는 작업이 시간상 가능할까 싶었다. 만일 가능하다고 하더라도 이 가공된 데이터가 정말 가성비가 좋다는 실질적인 의미를 담을 수 있을까? 혹시나 단순히 최저가만을 표시해주는 서비스가 되는 것은 아닐까? 이런 의문들이 끊임없이 생기면서 우리는 <strong>결국 주제를 변경하는 것으로 결론을 내렸다.</strong></p>
<h2 id="3일차-2am">3일차 2AM</h2>
<p>우리가 첫 아이디어 회의 때 안될 것 같다고 판단했던 두 개의 아이디어를 제외하고 <strong>두 개의 아이디어가 남아있었다.</strong> 하나는 <strong>AI 선물 추천 서비스</strong>였고, 다른 하나는 <strong>청년 정책들을 모아놓은 플랫폼 뿌리</strong>였다. 심지어 투표 결과는 2:2였고, 한 명은 선택을 보류하고 있었다.</p>
<p>다시금 아이디어 선택 기준으로 돌아가서, 내 경우에 선물 추천 서비스는 2번에 부합하지 않을 것 같았다. 사람들마다 선물하는 기준은 너무나도 다양하다. 인기 제품을 선물하는 사람도 있고, 비싼 제품을 선물하고 싶은 사람도 있을 것이고, 나 같은 경우에는 &#39;내 돈주고는 안 사지만 남이 사주면 한 번쯤 써볼만한 물건&#39;을 선물한다. 그리고 무릇 선물이란 누군가에게 선물하기 위해 선물을 고르는 시간에도 가치가 있다고 느끼는 사람으로서(<del>꼰대 아닙니다</del>) 뭔가 꺼림칙한 느낌이 들었다.</p>
<p>다음으로 청년 정책 플랫폼 뿌리는 3번에 부합하지 않을 것 같았다. 현재 청년 정책을 확인하기 위한 사이트들의 이용하기 불편한 점들을 개선한 UI를 구현할 뿐 특별히 기술적으로 도전 포인트가 있어 보이지는 않았다.</p>
<p>나는 이 중에 뿌리를 선택했다. 끌렸던 가장 큰 이유는 사실 이름에 있었다. <strong>청년들을 제주에 뿌리내리도록 돕겠다는 의미의 뿌리</strong>라는 이름이 확 와닿았다. 다음으로는 이 아이디어가 <strong>해커톤의 주제를 정면으로 관통하는 아이디어</strong>임은 분명했기 때문이다. 어떤 아이디어를 선택하더라도 우리보다 기술적인 수준이 높은 심사위원들을 <strong>기술 혁신의 관점에서 놀라게 하는 것은 굉장히 어려운 일</strong>이다. 그러면 기술 발전에는 기술 혁신만 있는가? <strong>기술 확산 또한 굉장히 중요한 기술 발전의 방향 중 하나</strong>이다. 내가 최첨단 신기술을 개발하는 자리에 있는 것이 아닌, 여러 기술들을 배우고 공부하는 <strong>팔로워의 입장에 있을 때는 기술 확산의 측면에서 기술 발전에 기여하는 것도 굉장히 중요한 일</strong>이라는 생각이 들어서 뿌리를 선택했다.</p>
<h3 id="콜럼버스의-달걀">콜럼버스의 달걀</h3>
<blockquote>
<p><em>그 특허발명의 진보성을 판단할 때에는 청구항에 기재된 복수의 구성을 분해한 후 각각 분해된 개별 구성요소들이 공지된 것인지 여부만을 따져서는 아니 되고, 특유의 과제 해결원리에 기초하여 유기적으로 결합된 전체로서의 구성의 곤란성을 따져 보아야 하며 이 때 결합된 전체구성으로서의 발명이 갖는 특유한 효과도 함께 고려하여야 한다.(대법원 2007. 9. 6. 선고 2005후3284 판결)</em></p>
</blockquote>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/c2f16110-9c10-45cd-be85-f69b862eb69e/image.jpg" width="60%"/>
</p>

<p>과거 변리사 시험을 준비하던 시절에 배웠던 특허법에서 <strong>어떤 특허가 새롭고 진보한 것인지 판단하는 기준</strong>에 이런 것이 있었다. A도 존재하고, B도 존재하는 것이면 A+B도 이미 있는 것이라고 판단할 것인가? 그렇지 않다. 이 때는 플러스(+) 하기가 얼마나 힘든지, 즉, <strong>구성의 곤란성</strong>을 따져보아야 한다. 실제 예시로는 <strong>돋보기가 달린 손톱깎이</strong>가 있었다. 돋보기도 있는 것이고, 손톱깎이도 있는 것이지만 아무도 시도하지 않았던 결합을 함으로써 새로운 가치를 창출한 것으로 인정받아 등록된 사례이다.</p>
<p>우리도 이번에 처음 알았지만, 제주도 청년 정책 중에 굉장히 좋은 정책들이 많이 있다. 하지만 그 정책들의 상세 내용을 보러 들어가는 과정이 굉장히 까다롭고, 안내가 친절하지 않다. 평소에도 정부에서 운영하는 사이트를 들어갈 때면 UI/UX에 대한 기대는 거의 제로(0)에 가깝다. 저마다의 사연이 있겠지만, 늘 그래왔다고 해서 바뀔 필요가 없는 것은 아니다. <strong>콜럼버스가 달걀의 한쪽 끝을 깨뜨려 달걀을 세웠듯이 이 특별할 것 없어 보이는 뿌리 서비스로 제주도를 바꿔보자</strong>고 팀원들을 설득했다.</p>
<h2 id="3일차-4am">3일차 4AM</h2>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/caef6244-6a22-4280-a020-90bef6b08d7f/image.jpg" width="60%"/>
</p>

<p>그렇게 우리 팀의 주제는 <strong>제주 청년 정책 플랫폼 &#39;뿌리(Ppoori)&#39;</strong>로 정해졌다.</p>
<br/>
<br/>
<br/>
<br/>
<br/>

<h2 id="또-다음편으로">또 다음편으로...</h2>
<p>글이 이렇게 길어질 줄은 몰랐는데, 쓰다보니 그 때의 감정이 새록새록 생각이 나서 자꾸 글이 길어지는 것 같습니다... </p>
<p>&lt;3편&gt;으로 찾아뵙겠습니다.ㅎㅎ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[더 늦기 전에 써보는 비전공자 개발자의 구름톤 in JEJU 10기 대상 후기! <1편>]]></title>
            <link>https://velog.io/@jiiker_/%EB%8D%94-%EB%8A%A6%EA%B8%B0-%EC%A0%84%EC%97%90-%EC%8D%A8%EB%B3%B4%EB%8A%94-%EB%B9%84%EC%A0%84%EA%B3%B5-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-%EA%B5%AC%EB%A6%84%ED%86%A4-in-JEJU-10%EA%B8%B0-%EB%8C%80%EC%83%81-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@jiiker_/%EB%8D%94-%EB%8A%A6%EA%B8%B0-%EC%A0%84%EC%97%90-%EC%8D%A8%EB%B3%B4%EB%8A%94-%EB%B9%84%EC%A0%84%EA%B3%B5-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-%EA%B5%AC%EB%A6%84%ED%86%A4-in-JEJU-10%EA%B8%B0-%EB%8C%80%EC%83%81-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Tue, 29 Oct 2024 19:52:15 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>저희 팀이 만들었던 <strong>뿌리 서비스</strong>에 대한 자세한 설명은 <a href="https://9oormthon.goorm.io/83182701-089a-4441-8589-c072b4102fe6">구름톤 전시관</a>에서 확인하실 수 있습니다.</p>
</blockquote>
<p>구름톤이 5월이었으니 벌써 다섯 달이 지났는데, 이제야 후기를 써본다... 벌써 구름톤 11기가 진행중이라는 소식을 듣고 11기 활동이 끝나기 전에는 써야하지 않을까 싶어 부랴부랴 벨로그를 켰다. 사실 벨로그를 시작할 때 구름톤 후기를 쓰고 싶어서 시작했었는데, 이래저래 바쁘게 살다보니(<del>는 핑계고..</del>) 너무 늦어져버렸다. 하지만, 더 늦기 전에 기억을 되살려 후기를 작성해보자!!</p>
<h2 id="구름톤이란">구름톤이란?</h2>
<blockquote>
<p><strong>구름톤(9oormthon)</strong>은 카카오 클라우드 플랫폼의 이름인 &#39;9rum&#39;과 구름의 영문명 &#39;goorm&#39;, &#39;Hackathon&#39;의 합성어</p>
</blockquote>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/ae835f2d-eb0b-4394-9baa-19c3f9ff1617/image.png" width="40%"/>
</p>

<p>구름톤은 3박 4일간 제주도에서 진행되는 해커톤으로 <strong>성산일출봉이 보이는 뷰에서 코딩할 수 있는 낭만 넘치는 해커톤</strong>이다!! 구름톤 트레이닝 풀스택 과정을 수료하고나서, 친해진 수료생 분들과 여러 해커톤들을 찾아봤는데, 이만큼 낭만 넘치는 해커톤이 없었기 때문에 나는 구름톤만 지원했다. 사실, 구름톤 트레이닝 과정을 수료한 사람들에게 가산점이 주어진다고 해서 주변에서 많이 합격할 줄 알았지만, 같이 지원한 사람들 중에서는 나 혼자만 합격했다...(나중에 비어파티 때 얼핏 듣기로는 20:1 정도의 경쟁률이라고 들었던 것 같다.)</p>
<h2 id="구름톤-지원-과정">구름톤 지원 과정</h2>
<p>300~500자 정도의 짧은 글을 쓰는 자소서 5문항으로 구성되어 있었고, 한참 상반기 공채 자소서를 열심히 쓰고 있을 때여서 크게 어렵지 않게 작성했던 것 같다. 최대한 <strong>내가 가진 장점</strong>(소프트스킬, 유머 있는 성격), <strong>개발에 대한 열정</strong>, <strong>성장에 대한 갈망</strong> 등을 열심히 어필했던 것 같다. 조금 까다로웠던 문항이 제주도의 사회문제를 해결하기 위한 아이디어를 내는 부분이었는데, 지금 생각해보면 굉장히 평이한 주제를 썼던 것 같다. 실제로 제주도에 가본지도 너무 오래 됐고, 제주도의 고령화 문제가 심각하다거나 제주 방언이 사라지고 있다는 등의 뉴스 기사들은 나에게 너무 멀게만 느껴졌기 때문에 어쩔 수 없었던 것 같다.</p>
<p>혹시나 만약에 <strong>&#39;내가 해커톤에 참여할 실력이 되나?&#39;, &#39;민폐가 아닐까?&#39;</strong> 하는 등의 걱정을 가진 분들이 계실까봐 간략하게 당시의 스펙을 적어보겠다.</p>
<blockquote>
<p><strong>&lt;벨로그 주인장 당시 스펙&gt;</strong></p>
</blockquote>
<ul>
<li>화학공학과 23년도 졸업(<strong>코딩 경험 전무</strong>, 교양 수업도 안 들음)</li>
<li>23년 1월 <strong>라피신 9기 2차</strong> 과정으로 코딩 공부 시작(라피신 시작 전에 인프런 &#39;홍정모의 따라하며 배우는 C언어&#39; <strong>조건문, 반복문, 문자 입출력</strong>까지 듣고 입과)</li>
<li>라피신 나름 열심히 함(로그인 시간 300시간 이상, 파이널 점수 60점, 본과정 <strong>불합격</strong>)</li>
<li>라피신 친구들과 자료구조, 알고리즘 스터디 결성</li>
<li>바킹독님 강의 들으면서 2개월 동안 백준만 풀어서 <strong>백준 골드레벨 달성</strong></li>
<li>하지만? 싸피 <strong>불합격</strong>, 네부캠 <strong>불합격</strong>, 기타 등등 <strong>불합격</strong></li>
<li><strong>구름톤 트레이닝 풀스택 2회차</strong> 과정 참여 및 <strong>수료</strong>(24년 2월 수료)</li>
<li>구름톤 트레이닝에서 <strong>프론트엔드</strong> 개발자로 <strong>팀 프로젝트 2회</strong> 수행</li>
</ul>
<p>이 정도의 화려한 불합격 이력을 가지고 있었고, 정말 기본적인 구현 능력만을 가지고 있었다. 그래도 구름톤 트레이닝에서 진행했던 두 번의 팀 프로젝트 경험을 통해 전반적인 웹서비스 개발이 어떻게 이루어지는지 정도는 파악하고 있었던 것 같다. 물론, 나도 다른 사람들에 비해 부족할 것이라는 생각을 가지고 있었고 똑같은 걱정을 하긴 했지만, 금세 마음을 고쳐먹었다.</p>
<blockquote>
<p><strong><em>&quot;동일한 것을 배운다면, 부족할수록 얻어가는 게 많다&quot;</em></strong>  - jiiker</p>
</blockquote>
<p>동일한 과정에 참여한다면, 문 닫고 들어온 사람일수록 승리자인 것이다. 배움에서만큼은 조금 이기적인 사람이 되어보자! 잘 하는 사람들 것을 빼앗고 내 것으로 만들어보자! 절대 실력이 부족하다고 주눅들지 말자. 무엇보다도 실력을 떠나서 <strong>이 낭만 넘치는 해커톤을 경험해보고 싶다면 무조건 지원해보자!!</strong></p>
<h2 id="구름톤-진행-과정">구름톤 진행 과정</h2>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/d9817706-c744-49c3-a12a-4cf4e1a91182/image.jpg" width="60%"/>
</p>

<p>구름톤 10기는 위와 같은 타임테이블을 따라 진행되었다. 첫 날에는 처음 3분 자기소개 세션과 여러 교육 세션들로 구성되어 있었고, 첫 날 마지막에 해커톤 주제가 발표된다. 그리고 둘째 날에는 오전의 교육 세션이 끝나고 바로 아이디어톤 및 팀 빌딩이 진행되었다. 팀 빌딩 이후에는 숙소까지 가는 데 시간이 꽤 걸렸고, 숙소 도착 이후에는 팀원들과 잠깐 회의를 진행하고 나서 바로 비어파티가 시작됐던 것 같다. 비어파티가 11시에 마무리 되었기 때문에, 사실상 개발을 할 수 있는 물리적 시간이 그렇게 많지는 않았다.</p>
<h3 id="3분-자기-소개">3분 자기 소개</h3>
<blockquote>
<p>_&quot;소개하기에 3분은 너무 부족한 사람&quot; _</p>
</blockquote>
<p>구름톤에 합격하고 나면, 첫 날에 있을 3분 자기 소개를 준비하라는 안내를 받는다. 3분? ENFP인 나에게 3분이라는 시간은 너무 짧았다. &#39;에이~ 그래도 3분 언저리로 주겠지~&#39;라고 생각했지만, 정말 타이머를 들고 오셔서 <strong>3분이 지나면 마이크를 가져가버리는 무자비한 자기 소개 세션</strong>이었다.</p>
<p>짧은 시간이긴 하지만, 이 때 주변 사람들에 대한 정보를 잘 파악해 두고, 팀을 이루고 싶은 사람들을 물색해 두어야 한다. &#39;2일차에 서로 알아가는 시간이 있겠지~&#39;라고 생각했다면 큰 오산이다. 2일차에 오전 교육 세션이 끝나고 나면 아이디어톤도 정말 후다닥 지나가고 갑작스러운 팀 빌딩이 시작되기 때문이다. 경험에서 우러나오는 아주 실질적인 조언이다.</p>
<p>나는 어렴풋이 생각해둔 아이디어가 게임적인 요소가 많이 가미된 아이디어여서 게임을 좋아하는 사람들을 주로 눈여겨 봤었다.(결국 그 분들과는 팀을 이루지는 못 했다.)</p>
<h3 id="교육-내용">교육 내용</h3>
<p>해커톤은 그냥 개발만 하는 프로그램인 줄 알았는데, 생각보다 교육 세션이 많고 다양하게 편성되어 있었다. 생각보다 많은 인사이트를 얻을 수 있었던 세션이었다. 특히 AI 교육 세션이 기억에 남았는데, AI에 대한 복잡하고 어려운 얘기가 아니었고, <strong>우리가 어떻게 AI를 실용적으로 활용할 수 있는지</strong>에 대한 얘기였다. 이전에 썼던 <a href="https://velog.io/@jiiker_/%EB%84%A4%EC%9D%B4%EB%B2%84-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BA%A0%ED%94%84-%EC%B1%8C%EB%A6%B0%EC%A7%80-%EC%88%98%EB%A3%8C-%ED%9B%84%EA%B8%B0">네이버 부스트캠프 후기</a>에서 생성형 AI로 그린 이미지를 넣을 생각을 할 수 있었던 것이 사실상 해당 세션 덕분이었다.</p>
<blockquote>
<p><em><strong>&quot;Done is better than perfect&quot;</strong></em></p>
</blockquote>
<p>&#39;바퀴가 네모라도 굴러가기만 하면 장땡이야<del>&#39;와 같은 얘기는 아니었고, 처음부터 완벽할 수는 없기 때문에 처음에는 결국 기능만이라도 잘 동작하는 프로토타입을 만들어야 하는데, 그 <strong>프로토타입의 기준과 방향성</strong>에 대한 이야기였다. 프로토타입을 만들더라도 그 <strong>기준과 방향성을 최종 목표와 일치하도록 설정</strong>해야 한다는 이야기였던 것 같다.(</del>아마 맞을 겁니다.~~) 사실 구름톤 트레이닝 과정 동안 네트워킹 행사에 매번 참여하면서 Wayne의 3부작 &#39;구르미가 성장하는 법&#39;에 대한 강연을 들었던 터라 굉장히 익숙한 얘기였다.</p>
<blockquote>
<p><em><strong>&quot;거인의 어깨에 올라타라&quot;</strong></em></p>
</blockquote>
<p>이 내용은 오픈소스 사용과 관련된 교육이었는데, 매번 멀리 내다보기 위해서 높은 산을 오르는 수고(직접 구현)를 들이지 말고, <strong>거인(오픈소스)의 어깨에 올라타서 멀리 바라보라는 그런 비유</strong>였던 것 같다.(<del>맞을 거에요.</del>) 특히나 시간이 부족한 해커톤과 같은 환경에서는 <strong>이미 잘 만들어진 오픈소스를 적극적으로 활용</strong>해보는 것도 좋다는 내용이었다.</p>
<p>아무튼 이렇게 교육 세션이 끝나고 대망의 해커톤 주제가 공개됐다.</p>
<h3 id="구름톤-10기-주제">구름톤 10기 주제</h3>
<blockquote>
<p><em><strong>#제주&nbsp;&nbsp;&nbsp;#클라우드&nbsp;&nbsp;&nbsp;#K-SDGs</strong></em></p>
</blockquote>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/f4b834a0-15c1-40b8-98da-81773652d7b6/image.jpg" width="70%"/>
</p>

<h3 id="k-sdgs-그게-뭔데">K-SDGs? 그게 뭔데?</h3>
<p>SDG(Sustainable Development Goals)는 <strong>지속가능발전목표</strong>를 뜻한다. 그래서 그게 뭡니까? </p>
<blockquote>
<p><em>&quot;지속가능발전목표(SDG, Sustainable Development Goals)는 인류의 보편적인 발전을 위해 전 세계 모든 국가가 2030년까지 달성하기로 합의한 변혁적인 목표를 말합니다. 이 의제 안에 사람(People), 지구(Planet), 번영(Prosperity), 평화(Peace), 파트너십(Partnership)에 기반한 17개의 목표와 169개 세부목표가 담겨져 있습니다.&quot;</em></p>
</blockquote>
<p>아주 거창한 주제가 등장해서 처음엔 당황스러웠지만, 카테고리가 너무 많아서 사실 어떤 주제든 다 가능할 것도 같았다. 그래서 기존에 어렴풋이 생각해 두었던 아이디어에 그럴싸한 주제를 갖다 붙여보기로 했다.</p>
<p>내가 생각했던 아이디어는 <strong>&#39;걸어서 땅따먹기&#39;</strong> 였다. 유저의 위치 데이터를 이용해서 경로를 그리고, 경로를 이용해 만들어진 도형 영역을 내가 차지할 수 있도록 하는 땅따먹기 게임과 같은 서비스를 구상했었다. 차지한 영역을 서로 뺏고 뺏기며 원초적인 경쟁심을 자극하는 그런 묘미가 있는 서비스이다.</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/jiiker_/post/4fa7586e-4329-4150-bfb9-b3807850c078/image.PNG" alt=""></th>
<th><img src="https://velog.velcdn.com/images/jiiker_/post/724f0138-fa22-4af8-838a-28b8fb084359/image.PNG" alt=""></th>
</tr>
</thead>
</table>
<p>이 아이디어를 통해 <strong>사람들이 많이 방문하지 않는 곳이 서비스를 이용하기엔 오히려 명소가 될 수도 있고</strong>, <strong>분기별 지도를 초기화 함으로써 지속적인 방문을 유도</strong>할 수도 있기 때문에 지속가능발전목표라는 주제와도 상당부분 부합하는 느낌이 들어서 굉장히 설레는 마음으로 아이디어톤을 준비하고, 그렇게 1일차를 마무리했다.</p>
<h3 id="팀-빌딩">팀 빌딩</h3>
<p>팀 빌딩 과정에서도 얘깃거리가 많이 있지만, 구체적인 이야기는 <a href="https://velog.io/@jiiker_/%EC%9A%B0%EB%8B%B9%ED%83%95%ED%83%95-%EB%B9%84%EC%A0%84%EA%B3%B5-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-%EA%B5%AC%EB%A6%84%ED%86%A4-in-JEJU-10%EA%B8%B0-%EB%8C%80%EC%83%81-%ED%9B%84%EA%B8%B0-2%ED%8E%B8">&lt;2편&gt;</a>에서 다루기로 하고, 내 아이디어에 대한 얘기만 하자면, 기각되었다...😥 정말 재밌고 신선한 아이디어라고 인정받긴 했지만, 사실 이 아이디어를 완성도 있게 구현하기 위해서는 프론트엔드 작업이 굉장히 중요해 보였는데, 나와 다른 프론트 개발자 둘 다 이 아이디어를 대략 30시간만에 구현한다는 것이 어렵겠다는 판단하에 기각해버렸다.</p>
<p>해커톤 얘기는 아니지만, 최근 벨로그 트렌드 피드에서 이 아이디어와 굉장히 유사해 보이는 서비스를 발견했는데, <a href="https://velog.io/@koomin1227/%EC%B4%88%EA%B8%B0-%EC%9C%A0%EC%A0%80-1000%EB%AA%85-%EB%AA%A8%EC%9C%BC%EB%8A%94-%EB%B2%95">땅따먹기 만보기 서비스 &#39;그라운드 플립&#39;</a>이라는 어플이다. 나중에 조금 여유가 생기면 한 번 사용해 봐야겠다. 나름 굉장히 애착이 가는 아이디어였던만큼 해당 서비스도 잘 되길 바란다..!! 멀리서나마 응원하겠습니다!</p>
<h2 id="다음편에-계속">다음편에 계속...</h2>
<p>글을 마무리하며 간략하게 근황을 남기자면, 네이버 부스트캠프 챌린지 수료 이후에 멤버십 과정에도 합격했다!! 하지만, 동시에 풀스택 개발자 인턴의 기회가 생겨서 멤버십 과정은 중도포기를 하게 되었다... 그래서 지금은 미생의 삶을 살면서 예전에 받았던 외주 프로젝트를 병행하며 매우 바쁘게 살아가고 있다.</p>
<h3 id="다음편-미리보기">다음편 미리보기</h3>
<p align="center">
<image src="https://velog.velcdn.com/images/jiiker_/post/8b5dcf32-f0d6-4c91-b2b2-666665cd0c01/image.jpg" width="90%"/>
</p>


]]></description>
        </item>
        <item>
            <title><![CDATA[[웹 풀스택] 네이버 부스트캠프 챌린지 수료 후기]]></title>
            <link>https://velog.io/@jiiker_/%EB%84%A4%EC%9D%B4%EB%B2%84-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BA%A0%ED%94%84-%EC%B1%8C%EB%A6%B0%EC%A7%80-%EC%88%98%EB%A3%8C-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@jiiker_/%EB%84%A4%EC%9D%B4%EB%B2%84-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BA%A0%ED%94%84-%EC%B1%8C%EB%A6%B0%EC%A7%80-%EC%88%98%EB%A3%8C-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Tue, 13 Aug 2024 09:13:16 GMT</pubDate>
            <description><![CDATA[<p>이번 주 회고 썸네일은 부스트캠프에서의 활동을 마치고 <strong>정리</strong>하는 느낌으로 준비해봤다. 수많은 시도 끝에 탄생한 그림이다. 가방에 책을 정리하는 느낌으로 그려달라고 했는데 캐릭터가 지금 뭘 하고있는 건지는 모르겠다.(챌린지 과정에서의 실제 내 모습인가?) 뭘 써놨는지 알 수 없는 노트는 마치 실제 내 학습정리를 반영한 것 같기도 하다... 그리고 책의 제목들을 몇 개 알려줬는데, 마치 부스트캠프의 비밀유지의무를 의식한 것 마냥 알아볼 수 없게 써놓은 점도 은근히 마음에 들었다.</p>
<p>아무튼 어느덧 4주 간의 부스트캠프 챌린지 과정이 끝이 났다. 모든 일들이 다 비슷하겠지만, 끝이 나면 후련하기도 하면서 어떤 부분에는 아쉬움이 남기 마련인 것 같다. 4주차 생활과 함께 챌린지 전반에 대한 회고를 진행 해보자!</p>
<h2 id="4주차-생활">4주차 생활</h2>
<p>4주차를 시작하면서 마음먹었던 건, 여기서 새롭게 익혔던 것들을 정리하고 나가는 것이었다. 과제를 완성해서 낸다기 보다는 계획대로 진행하는 것을 우선적으로 했다. 과제를 받으면, <strong>요구 사항을 분석</strong>하고, <strong>체크리스트를 설정</strong>하고, <strong>학습</strong>을 하고, <strong>설계</strong>를 하고, <strong>구현</strong>을 하고, <strong>테스트</strong>를 진행한다. 사실 당연한 것 아니냐고 생각할 수는 있지만, 늘 그렇듯 당연한 것들을 지키는 게 어렵다. 그 당연한 것들을 <strong>체득(體得)</strong>하는 것이 이번 주차의 목표였다.</p>
<p align="center">
  <image src="https://velog.velcdn.com/images/jiiker_/post/bc3669ee-a8ea-4ce4-8253-c955b10f4093/image.png" width="60%"/>
<p/>


<h3 id="짝-개선하기--짝-해결">짝 개선하기 / 짝 해결</h3>
<p>3주차에는 짝 활동들을 뭔가 취지에 맞게 진행하지 못 했던 것 같아서 4주차 짝 활동에서는 지시사항을 최대한 이행하려고 했다. 짝 개선하기의 경우에 처음 배정 된 팀원이 참여를 못 하게 되어서 다른 팀에 들어가 3명이 함께 진행하게 됐는데, 굉장히 반겨주셔서 감사했다.(<del>이게 다</del> 피어세션에서 열심히 해서 그런 거라니까요?~~)</p>
<p><strong>짝 개선 세션</strong>에서는 내 코드가 아닌 다른 사람 코드의 개선점을 찾아주는 것이다 보니 정말 세심하게 상대방의 코드를 살펴볼 필요가 있다. 지난 주엔 각자 준비해 온 개선방향에 대해 토론을 나눈 것에 그쳤던 반면에 이번엔 한 명씩 돌아가며 VS Code의 Live Share를 이용해서 피드백을 받았다. 한 명의 코드를 피드백 하는데 한 시간 이상 소요됐고, 굉장히 꼼꼼한 피드백을 주고 받을 수 있었다. 특히, 다른 사람의 코드를 세심하게 살펴보다 보면 <strong>문제 해결에 대한 새로운 접근 방식을 터득</strong>하게 되는 경우가 굉장히 많다. 그래서 이번 개선하기에서는 특히 다른 캠퍼들의 해결 방식을 참고하여 코드를 개선했던 부분이 몇 군데 있다.</p>
<p>마찬가지로 <strong>짝 해결 세션</strong>에서도 지난 주에 아쉬웠던 부분을 보완해서 진행해 보려고 했다. 설계까지는 함께 진행했지만, 구현부터는 <strong>드라이버와 네비게이터 역할을 나누어 20분 단위로 역할을 바꿔가며 진행</strong>했다. 개인적인 생각으로 이 과제를 할 때 팀원 분과의 팀워크가 상당히 좋았던 것 같다. 서로 스타일 자체가 상호보완적이기도 했던 것이 팀원분은 굉장히 세세한 부분을 잘 캐치하시는데, 그 반면에 나는 좀 더 큰 그림을 보는 능력이 좋았던 것 같아서 그런 부분들이 시너지가 좋았던 것 같다. 그래서 나는 &quot;어떤 함수는 어떻게 구현하면 되겠다.&quot; 정도의 큰 그림을 제시해드리면, 팀원 분은 해당 함수의 예외 상황들을 잘 캐치해주셨던 것 같다.</p>
<p>짝 해결 세션에서 경험한 방식은 정말 새로운 방식이긴 했지만, 잘 맞는 짝과 수행해서 그런지 정말 재미있었다. 정말 &quot;이게 되나?&quot; 싶던 것들도 어느새 &quot;이게 되네?&quot;로 바뀌어 있었다. 아마도 <strong>혼자서 생각했을 땐 모호하던 부분을 팀원이 잘 보충</strong>해주면서 좋은 결과로 이어졌던 것이 아닐까 생각해본다.</p>
 <p align="center;">
  <img src="https://velog.velcdn.com/images/jiiker_/post/797625d6-ac99-4605-b7ce-b2d9a5a6a9ef/image.png" width="30%" alt="Image"/>
 </p>


<h3 id="피어세션">피어세션</h3>
<p>4주차에 기본적인 개발 프로세스를 체득하는 것과 더불어 평소에 <strong>귀찮아서 대충 하던 것</strong>들, <strong>어렵다고 뒤로 미뤄둔 것</strong>들을 적극적으로 찾아 해보려고 했다. 그 중에 하나가 피어세션에서 코드리뷰를 정말 꼼꼼하게 해보는 것이었다. 다른 사람의 코드를 읽는 것은 어렵다. 그렇기 때문에 이 또한 연습해보자는 취지에서 시작했다. </p>
<p>피어세션 때 마지노선 타이머를 켜두고 그 시간이 넘기 전까지는 코드 한 줄 한 줄 꼼꼼하게 읽었고, 실행이 되는지 여부도 꼼꼼하게 체크했다. 실행이 안되더라도 바로 No로 평가하지는 않았고, 윈도우 호환성 문제인지도 고민 해보고, 코드를 읽어봤을 때 어느 정도 타당성이 있으면 Yes로 평가했던 것 같다. 그리고 평가 중에 문제가 있었던 부분들은 노트에 기록해뒀다가 피어세션에서 얘기를 나누었다. 이런식으로 진행했더니 피어세션에서 얘기할 거리도 굉장히 풍부해지고, 다른 사람의 코드를 읽는 실력도 많이 늘었던 것 같다.</p>
<p>무엇보다도 팀원들이 굉장히 고마워했다. 그렇다. 내 코드를 누군가가 열심히 읽어봐주는 것은 굉장히 고마운 일이다. 나 또한 3주차에 다른 캠퍼 분께 고마움을 느꼈었기 때문에 실천해 본 것도 있다. 그리고 노력해서 읽다보면 생각보다 잘 읽힌다!(<del>하면 잘 하는 놈이 꼭 화를 내야...</del>) 또 나중에 개선된 코드에 내 피드백이 반영되어 있는 것을 보면 뭔가 모를 뿌듯함이 밀려온다.</p>
<h3 id="3차-문제해결력-테스트">3차 문제해결력 테스트</h3>
<p>과제나 시험 내용에 관해서는 비밀유지의무가 있기 때문에 자세히 말할 수는 없지만, 확실히 캠퍼들이 지난 기간 동안 챌린지 전반의 활동을 잘 수행했는지 평가할 수 있었던 테스트였던 것 같다. 물론, 나는 챌린지 활동을 잘 수행했다고 생각했지만 시험은 시원하게 말아먹었다!</p>
<p>시험을 보며 들었던 <strong>한 가지 생각</strong>이 있다. 챌린지 과정은 엄밀하게 따지면 <strong>코어타임이 10시(12시 과제 공개) ~ 19시</strong>이다. 하지만 앞선 캠퍼들의 후기들을 보면 챌린지 과정의 혹독함은 굉장히 악명(?)이 높다. 그래서인지 다들 당연하다는 듯이 밤을 새고, 나 또한 매일 밤 샐 각오로 임했다. 그러다보니 저녁 7시 전에 과제를 끝내겠다는 생각은 엄두도 못 냈다. 그런데 분명한 것은 과제를 받을 때부터 &#39;나는 오늘 밤을 샐거야.&#39;라는 생각을 가지고 있으면, 긴장이 풀리면서 굉장히 루즈해진다. 하지만 부스트캠프는 <strong>캠퍼들이 코어 타임 내에 과제를 끝내는 것을 목표로 하길</strong> 또 <strong>그렇게 성장하길</strong> 바랬을 수도 있겠다는 생각을 했다. </p>
<h3 id="릴레이-미션">릴레이 미션</h3>
<blockquote>
<p><strong>&lt;미션&gt; 수료식 정장 입고 참여하기 🍷와인잔 필수🍷</strong></p>
</blockquote>
<p>왜 이런 미션을 골랐을까?(내가 직접 고른 게 맞다) 사실 어떤 시험 같은 게 있을 때 그 전에 뭔가 신경써야하는 것들이나 &#39;시험 끝나고 뭐 하자!&#39; 같은 약속을 안 잡는 편이다. 시험 전에 최대한 신경 쓰일만한 것들을 배제하려고 고른 것도 있지만... 왜 이런 미션을 골랐을까?</p>
<p>기왕 골랐으니 하긴 해야겠고, 테스트 끝나고 슬랙 분위기를 보니 축제보다는 초상집에 가까웠다. 그래서 어두운 색상의 정장으로 예를 다했다. 면도도 못한 몰골이기에 얼굴은 가리도록 하겠다...</p>
<p align="center">
    <image src="https://velog.velcdn.com/images/jiiker_/post/f766c32c-f517-494c-9042-55ac79c0cfe0/image.jpg" width="50%"/>
<p/>

<p>그래도 끝났으니 한 잔 해~</p>
<h2 id="챌린지-수료-회고">챌린지 수료 회고</h2>
<p>개인적으로 테스트와 수료식 날을 나누었으면 어땠을까 하는 생각이 든다. 뭔가 테스트도 그랬지만 수료식까지 정신 없이 진행되었던 것 같은 느낌이다. 12시까지 피어세션을 진행하고 1시까지 점심을 먹은 후 1시부터 테스트가 시작이었는데, 12시에 올라온 시험 공고를 보고 밥 먹다 체할 뻔 했다. 모바일로 확인하다가 확인할 사항이 한 두 개가 아니어서 먹던 햄버거를 허겁지겁 먹고나서 집으로 돌아왔다.</p>
<p>이 사소한 불평 외에는 챌린지 과정을 진행하며 다른 불만은 없었던 것 같다. 굉장히 잘 짜여진 교육 과정이라는 느낌을 많이 받았고, 덕분에 굉장히 빠른 시간 내에 많은 것들을 배웠다.</p>
<h3 id="얼만큼-성장-했을까">얼만큼 성장 했을까?</h3>
<p align="center">
    <image src="https://velog.velcdn.com/images/jiiker_/post/15ebbb89-1951-4ea6-b437-fa489964cbe5/image.png" width="50%" />
<p/>

<p>게임처럼 수치로 나타나면 정말 좋을텐데, 정말 아직은 잘 모르겠다. 하지만, 앞으로가 정말 중요할 것 같다. 현재는 그 동안 배운 것들이 머릿속에 어지럽게 떠 다니는 느낌인데, 이를 깔끔하게 정리하는 시간을 반드시 가져야겠다. 다만 하나 확실한 건, 어떤 문제를 마주했을 때 더 이상 당황하지는 않을 것 같다. 4주간 매일이 당황의 연속이었고, 그 <strong>당황스러움을 대처하는 방법</strong>을 배웠다.</p>
<h3 id="★초단기★-빠르게-성장하는-초특급-비법-★바로-합격★">★초단기★ 빠르게 성장하는 초특급 비법 ★바로 합격★</h3>
<p>같은 건 없다...</p>
<blockquote>
<p><strong>*&quot;쉽게 얻을 수 있는 모~~~~~든 것들은 다 나쁜 것이야.&quot;  - 메가스터디 손주은 -*</strong></p>
</blockquote>
<p><a href="https://youtu.be/hhxrEDl13DY?si=L80HyLKqA20if_Vf&t=1h13m54s" target="_blank">손주은 쓴소리 영상</a> 후반부에 나오는 얘기는 워딩이 많이 쎄시긴 하지만, 굉장히 공감하는 내용이다. 물론 고3 혹은 N수생 등 특수한 상황에 놓여있는 경우에 한정해서 하는 말이다. 하지만 성장을 위해 학습하고 있는 상황이라면 어느 정도 통용될 수 있다고 본다.</p>
<p>릴레이 미션에서 난 &#39;소프트웨어 장인&#39;이라는 책을 읽었지만, <strong>&#39;개발자 원칙&#39;</strong>을 읽었던 동료 캠퍼 분들이 얘기해주신 것 중에 그런 얘기가 있었다.</p>
<blockquote>
<p><strong>*&quot;개발자는 평생 공부하는 직업입니다. 새로운 지식이나 새로운 분야, 그리고 신기술을 탐험하는 숙명을 가진 직업이 개발자입니다. 언젠가는 익숙한 도구를 익숙하지 않은 다른 도구로 바꾸거나, 익숙한 라이브러리를 대신해서 새로운 라이브러리를 사용해야 합니다.&quot; - 개발자원칙(박성철, 2022) - *</strong></p>
</blockquote>
<p>개발자로 예를 들면, 계속해서 익숙한 도구를 사용하는 것이 <strong>&#39;쉽게 얻을 수 있는 것&#39;</strong>에 해당할 수 있지 않을까 생각해 본다. 개발자라는 직업 특성상 내가 작성하고 있는 코드들은 언제든 레거시 코드가 될 수 있다. 그렇기 때문에 더욱 트렌드에 민감해야 하고, 그 트렌드를 좇을 준비가 되어 있어야 한다. </p>
<p>챌린지를 하며 비슷한 것을 느꼈는데, 이번에 <strong>챌린지 과정을 하면서 언제 성장했음을 느꼈는지</strong> 물어본다면, 이렇게 대답할 수 있을 것 같다. &#39;나중에 시간나면 해봐야지&#39;라고 생각하던 것들의 <strong>&#39;나중&#39;을 &#39;지금&#39;으로 바꿀 때</strong> 성장을 많이 체감했던 것 같다. 귀찮고 어려운 것이라고 나중으로 미루게 되면 한없이 미루게 될 뿐이다. 지금 당장 시작하자.</p>
<h3 id="생성형-ai에-관하여">생성형 AI에 관하여</h3>
<blockquote>
<p><strong>ChatGPT는 성장에 도움이 되는가?</strong></p>
</blockquote>
<p>사실 GPT가 없었다면 부스트캠프 과정을 반도 못 따라갔을 수도 있다. 아무리 당황스러운 과제가 나오더라도 마음 한켠에 GPT가 있다는 사실에 든든함을 느끼고 있었다. 그렇기 때문에 GPT가 과정을 진행함에 도움이 되었다는 사실은 부정할 수가 없다.</p>
<p>챌린지 3일차쯤 ChatGPT 4.0을 결제할까 고민했던 적이 있었다. 첫 날 과제만 조금 수월했고, 그 이후로 과제들의 난이도가 계속해서 상승했기 때문에 GPT의 힘을 빌려야 하나 고민했던 것이다. 하지만 그러지 않았다. ChatGPT 4.0을 결제하고 나면 <strong>GPT에 대한 의존도가 높아질 것이라 생각했고, 그 부분을 경계해야 한다</strong>고 생각했기 때문이다.</p>
<p>공식 문서에서 정보를 습득하는 것과 GPT에서 검색한 내용을 읽는 것의 차이는 무엇일까? 나는 개인적으로 정보 습득의 측면에서는 크게 차이가 없을 것이라고 생각한다. 공식 문서를 통해 알게되는 것이나 GPT를 통해 알게되는 것이나 똑같은 정보를 알게 되는 것이다. </p>
<p>그 차이는 <strong>시간</strong>에 있다고 생각한다. 공식 문서와 같은 low level의 정제되지 않은 데이터를 읽기는 어렵고 그 만큼 이해하는 데에 시간이 많이 소요된다. 하지만, GPT를 통해 검색하게 되면 큰 노력 없이 정제된 데이터를 제공해준다. 그렇다면 시간이 많이 소요되는 것이 좋은 것인가? 꼭 그렇지만은 않다. <strong>정제되지 않은 데이터를 읽는 것은 힘들기 때문에 어쩔 수 없이 시간이 많이 소요되고 그 과정에서 자연스럽게 내 것으로 만드는 과정이 생기는 것</strong>인데, 어떻게 보면 결과에 비해 너무 많은 시간이 투자될 수도 있는 것이다. 반면에 <strong>GPT를 사용하게 된다면 그 &#39;내 것으로 만드는 과정&#39;이 포함될 수 있는 시간이 압도적으로 부족</strong>하다.</p>
<p>GPT를 사용하는 것이 도움이 되느냐고 물어보면, 뭐라고 한 번에 깔끔하게 대답하기는 어려운 것 같다. 다만, 효율과 비효율 사이에서 <strong>&#39;내 것으로 만드는 과정&#39;을 찾을 수 있도록 중도(中道)를 지키는 것</strong>이 중요해 보인다.</p>
<h3 id="앞으로의-계획">앞으로의 계획</h3>
<p align="center">
    <image src="https://velog.velcdn.com/images/jiiker_/post/5a740091-5bce-48fc-8ea9-8ab3b803d3f6/image.PNG" width="50%" />
<p/>

<p>우선 당분간은 하반기 취업 준비를 하면서 남는 시간을 활용해서 못다 한 학습정리들을 조금 채워보려고 한다.</p>
<p>그리고 외주로 프로젝트를 하나 진행하게 되었는데, 부스트캠프를 시작하기 전에는 이 프로젝트에 대해 &#39;우리가 이걸 할 수 있을까?&#39; 하는 막연한 회의감 같은 게 있었는데, 챌린지를 마치고 나서 갑자기 근거 없는 자신감이 생기더니 계약을 덜컥 해버렸다. 지금 상태라면 어떤 과제라도 해결할 수 있을 것만 같다.</p>
<p>챌린지를 통해 지식적인 측면에서도 많이 성장했지만, 정신적으로도 많이 단단해진 것 같다. 챌린지 과정이 고되긴 했지만, 끝나고 쉬는 것이 아닌, <strong>챌린지를 원동력 삼아 앞으로 좀 더 나아가 보려고 한다.</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[네이버 부스트캠프 웹,모바일 9기 베이직 - 
합격 및 1일차 후기]]></title>
            <link>https://velog.io/@jiiker_/%EB%84%A4%EC%9D%B4%EB%B2%84-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BA%A0%ED%94%84-%EC%9B%B9%EB%AA%A8%EB%B0%94%EC%9D%BC-9%EA%B8%B0-%EB%B2%A0%EC%9D%B4%EC%A7%81-%ED%95%A9%EA%B2%A9-%EB%B0%8F-1%EC%9D%BC%EC%B0%A8-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@jiiker_/%EB%84%A4%EC%9D%B4%EB%B2%84-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BA%A0%ED%94%84-%EC%9B%B9%EB%AA%A8%EB%B0%94%EC%9D%BC-9%EA%B8%B0-%EB%B2%A0%EC%9D%B4%EC%A7%81-%ED%95%A9%EA%B2%A9-%EB%B0%8F-1%EC%9D%BC%EC%B0%A8-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Mon, 12 Aug 2024 16:00:55 GMT</pubDate>
            <description><![CDATA[<h2 id="🥳부스트캠프-합격">🥳부스트캠프 합격!!</h2>
<p>요즘 하반기 자소서도 이곳저곳 넣고는 있지만 연락이 오는 곳은 없고... 어찌저찌 네이버 부스트캠프는 합격하게 됐다!! 작년 이맘때쯤 8기에 지원했을 때 코딩테스트 첫 문제가 하드코딩인 줄 모르고 재귀로 하노이탑을 구현하다가 0솔로 광탈했던 것에 비하면 나름 발전이 있었던 것 같기도 하다!! 개인적인 느낌으로는 베이직 과정이 추가되면서 1차 코딩테스트의 난이도는 조금 내려간 것 같은 느낌이었다. 이번엔 직접적으로 CS 지식을 물어보는 문제는 없었고, 약간의 논리력을 요하는 문제들이 6문제 정도 나왔던 것 같고, 알고리즘 문제 3문항(+ 알고리즘 첫 번째 문제는 어떻게 풀었는지 풀이를 적는 문항이 하나 더 있었던 것 같다)으로 구성되어 있었다.</p>
<h3 id="코딩테스트-환경">코딩테스트 환경</h3>
<p>조금 특이했던 것이 프로그래머스가 아닌 <strong>구름 환경</strong>에서 코딩테스트가 진행되었다! 나름 구름톤 트레이닝과 구름톤(해커톤)을 통해 성장한 사람으로써... 구름이 무럭무럭 자라나는 모습을 보니 어깨에 힘이 들어가는 것 같았다..!!(<del>구름뽕이 마구마구 차오른다!!!!</del>)</p>
<p>그런데 주변 환경을 촬영하거나 카메라를 셋팅하는 과정이 없었는데, 이 부분이 안내 페이지에는 나와있는 것을 보면 아직 개발이 완전한 것은 아닌가 싶은 생각이 들었다.</p>
<p>그래도 코딩테스트를 치르면서 불편한 점도 없었고, 프로그래머스보다 괜찮은 점도 있었다!! 기억에 남았던 것은 프로그래머스는 메인 함수를 보여주지 않고 solution 함수만 짜도록 되어있는 반면에 구름은 메인 함수도 다 나와있지만 편집만 불가능하게 설정되어 있었던 점인데, 나는 이 부분에서 구름 방식이 좀 더 마음에 들었다.</p>
<h3 id="코딩테스트-난이도">코딩테스트 난이도</h3>
<p>알고리즘 문제들은 전반적으로 <strong>백준 실버3 ~ 실버1 정도</strong>의 문제가 아니었을까 싶다(골드는 아닐거라고 생각하는 이유는 시간복잡도를 고려하지 않아도 됐던 것 때문에). 다만, 이미 짜여져 있는 코드를 토대로 요구사항에 맞춰 코드를 추가하는 방식으로 되어있었다. 특별한 알고리즘을 이용해야하는 문제는 없었고, 전부 구현 문제였던 것 같다. 3문제를 순서대로 풀었고, 시간은 거의 딱 맞춰서 끝냈던 것 같다.</p>
<p>특히 세 번째 문제에서 내가 작성한 코드에 따르면 2차원 데이터를 첫 번째 요소에 대해서는 내림차순, 두 번째 요소에 대해서는 오름차순으로 정렬을 해야했다. 아무 생각 없이 STL sort 함수(C++)를 사용했는데, 종료 5분을 남기고 이 부분을 인지했다. compare 함수를 짜서 sort 함수의 세 번째 인자로 전달해줄 수 있다는 건 알지만... 한 번도 짜본 적이 없었다...</p>
<p>포기하고 그냥 제출할까 싶기도 했지만, &#39;시간 복잡도는 고려하지 않아도 됩니다&#39;는 문구가 눈에 들어왔다. 그래서 막무가내로 버블정렬을 구현했다. 첫 번째 요소를 기준으로는 내림차순... 두 번째 요소를 기준으로는 오름차순... 제대로 썼는지도 모르게 허겁지겁 작성해서 마감 20초전 누른 제출에서 컴파일 에러가 발생했다... 이젠 진짜 끝이다 싶던 찰나 보이는 괄호 오타... 그 오타를 수정하며 무교이지만 여러 신들을 찾았던 것 같다. 내 부름에 답한 것은 라피&#39;신&#39;이 아니었을까 싶다. 과거 라피신 과정(42서울 예비과정)에서 버블정렬을 <del>100만번 정도</del> 구현했었는데, 이 경험 덕분에 빠르게 구현할 수 있었다고 생각한다. 그렇게 어찌저찌 알고리즘 3문제 전부 테스트케이스가 통과되는 것을 확인하고 제출했다.</p>
<h3 id="이거-급행이에요">이거 급행이에요?</h3>
<p>1차 문제 해결력 테스트에 합격하면 당연히 2차 문제 해결력 테스트 대상자가 되겠거니 생각하고 그냥 일반 합격 메일로 알고 있었다. 오늘 같이 합격한 친구가 2차 문제 해결력 테스트 대상자 선정 됐냐고 물어봐서 알게됐다. 그러고 메일을 자세히 뜯어보니 &#39;베이직 입과를 <strong>_권장 _</strong>합니다.&#39;라는 문구가 눈에 들어왔다. 그 친구가 알고리즘 2솔이었던 것을 보면 2차 테스트 직행 티켓의 커트라인은 알고리즘 3솔이 아니었을까 싶다. <del>나는 3솔인 것이 분명하다.</del></p>
<hr>
<h2 id="🗓1일차-후기">🗓1일차 후기</h2>
<p>오전에 간단하게 오티를 진행하고, 12시에 오늘의 미션이 공개됐다. 과제 내용은 크게 어려운 건 없었고, Git gist를 사용하는 게 미숙해서 조금 헤맸다. Git을 레포지토리를 만들고 로컬 폴더와 연동하는 방식으로만 사용했는데 이런 기능이 있다는 것을 처음 알게됐다. </p>
<h3 id="gist">Gist</h3>
<p>gist는 풀 프로젝트의 공유가 아닌 간단한 코드 조각을 공유할 때 사용하는 기능이다.</p>
<p>우측 사이드바를 열어 Your gists에 들어가면 작성했던 gist들을 볼 수 있다.</p>
<p align="center">
    <image src="https://velog.velcdn.com/images/jiiker_/post/40276126-df34-4664-8b0b-37fc3d91bae5/image.PNG" width="30%"/>
</p>

<p>다음 우측 상단의 &#39;+&#39; 버튼을 눌러서 새로운 gist를 작성할 수 있다.</p>
<p align="center">
    <image src="https://velog.velcdn.com/images/jiiker_/post/a1be0ad6-0939-4617-8c1e-5b4d78588aae/image.PNG" width="30%"/>
</p>

<p>gist에 대한 discription을 쓰고, 파일명을 확장자까지 포함하여 써준 다음 내용을 작성하면 된다. 이 때, 파일명을 README.md 로 작성해주면 마크다운 형식을 지원하고, git에서 리드미를 작성할 때 처럼 미리보기가 지원되는 양식으로 변경된다. </p>
<p align="center">
    <image src="https://velog.velcdn.com/images/jiiker_/post/058aa25c-378b-474b-92b0-e11e26414440/image.PNG" width="60%"/>
</p>

<p>그리고 새로운 파일을 추가하고 싶으면 좌측 하단에 있는 &#39;Add file&#39; 버튼을 클릭해서 추가할 수 있다.</p>
<h3 id="과제를-하며-느낀점">과제를 하며 느낀점</h3>
<p>과정 자체가 Basic 이기도 하고, 오리엔테이션에서도 언어에 대한 경험이 없더라도 과정만 잘 따라오면 합격이 가능하다고 하여서 정말 기초적인 것들을 배울 줄 알았다. 하지만 첫 과제를 보고 든 생각은 기초는 알아서 해야겠구나 싶은 생각이 들었다. 생각보다 실무적인 문제 해결 방식을 요구하고 있고, 그 과정에서 쓰이는 기초는 알아서 학습해야 하는 것 같았다. 마치 42서울의 라피신과 유사한 느낌이 들었다. </p>
<p>만약 이전에 어떤 교육과정도 없이 처음으로 부스트캠프에 입과했다면, 1일차에 적지않게 당황했을 것 같기도 하다. 하지만 그만큼 이미 어느 정도 숙달이 되어있는 사람도 자신만의 도전포인트를 만들어가며 베이직에서 함께 성장할 수 있도록 과정이 설계됐다는 느낌을 많이 받았다. 오늘은 개인 사정상 과제에 시간을 많이 투자하지는 못 했지만, 다음 과제부터는 시간을 좀 들여서 고민 해보고 해결해 봐야겠다.</p>
<h3 id="필자의-한마디">필자의 한마디</h3>
<p>혹시 같이 과정을 진행하고 계신 분이 있다면(아니더라도 누구든지!!) 연락은 언제든 환영입니다!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[웹 풀스택] 네이버 부스트캠프 챌린지 3주차 회고]]></title>
            <link>https://velog.io/@jiiker_/%EB%84%A4%EC%9D%B4%EB%B2%84-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BA%A0%ED%94%84-%EC%B1%8C%EB%A6%B0%EC%A7%80-3%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@jiiker_/%EB%84%A4%EC%9D%B4%EB%B2%84-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BA%A0%ED%94%84-%EC%B1%8C%EB%A6%B0%EC%A7%80-3%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sun, 04 Aug 2024 14:01:36 GMT</pubDate>
            <description><![CDATA[<p>이번 3주차의 키워드는 <strong>&quot;함께&quot;</strong>였던 것 같아서 이 주제를 잘 담아낼 수 있는 그림을 그려보았다. 벨로그 썸네일을 위해 이미지 생성 AI를 여러가지 사용해봤는데, 개인적으로 <a href="https://ideogram.ai/t/explore">ideogram</a>이 내가 원하는 조건을 잘 반영해서 그려주는 것 같다! 앞으로 이 사이트를 종종 써야겠다. </p>
<h2 id="3주차-생활">3주차 생활</h2>
<p>2주차까지의 생활을 하면서 느꼈던 것이 마냥 밤새면서 구현을 끝낸다고 해서 의미있는 부스트캠프 생활이 될 것 같지는 않았다. 그래서 3주차에서는 새로운 것들을 시도해 보고자 마음을 먹었다. 과제들을 진행하면서 클래스 다이어그램을 깔끔하게 그리신 분들의 리드미를 볼 때 마냥 감탄만 하고 있었는데, 그럴 게 아니라 나도 직접 그려보면서 설계를 해보자고 마음을 먹었다. 물론 안해본 것들을 하려고 하면 처음에는 시간이 많이 소요될 수 밖에 없다. 하지만 할 줄 아는 것을 하면서 밤을 새는 것보다 완성과는 좀 멀어지더라도 새로운 걸 도전해보는 것이 더 가치있을 것이라 생각했다.</p>
<h3 id="개인-해결--같이-개선">개인 해결 + 같이 개선</h3>
<p>솔직히 이 과제는 부스트캠프에서 의도한 대로 수행하지는 못 했던 것 같다. 아니, 어떤 의도인지 정확하게 파악하지 못 했다고 말하는 것이 더 정확할 것 같다. 지금 생각해 보면, 이 과제를 통해서 다른 사람의 코드를 읽고 이해하는 능력을 키울 수 있는 좋은 과제라는 생각이 든다. 같이 개선하기는 시간을 들여 상대방의 코드를 자세히 살펴보고 이해한 내용을 바탕으로 개선 방안을 제시하기를 의도했을 것 같다. 하지만, 우리 팀은 각자가 어떤 부분을 개선하고 싶은지 준비해와서 그 개선 방안이 적절한지 등에 관한 토론 정도만 진행했던 것 같다. 뭔가 무의식 중에 전자의 방법은 너무 시간이 많이 걸리고 힘들 것 같다는 이유로 배제했던 것 같다. 하지만, 나는 지금 &#39;챌린지&#39; 과정에 있지 않은가? 힘들어야 얻어갈 것이 있다던 마스터님의 말씀을 되새기며 다음주에는 정말 challenge를 해봐야겠다.</p>
<h3 id="짝-해결--각자-개선">짝 해결 + 각자 개선</h3>
<p>그러고 보니... 짝 해결 과제 또한... 너무 우리가 편한대로 진행했던 것 같다. 짝 해결 과제 설명에 20분 단위로 드라이버 역할과 네비게이터 역할을 바꿔가며 수행하라는 지시사항이 있었는데, 이 부분이 크게 중요하지 않은 것 같다고 생각해서 별 생각 없이 진행했다. 하지만, 이후 피어세션에서 실제로 이렇게 진행했던 팀과 얘기를 나누어 보니 빠르게 역할을 전환하는 것이 굉장히 중요한 지시사항이었던 것 같다는 생각이 들었다. 스터디 조원 분들 중 이 부분을 잘 수행했던 팀이 있었는데, 역할 전환을 자주 함으로써 계속해서 전체적인 흐름을 놓치지 않고 따라갈 수 있었던 점이 좋았다고 소감을 남겨주셨다.(다만, 굉장히 체력 소모가 심했다고 한다...) 다음 주 짝 해결 과제에서는 이 부분을 반드시 경험해 봐야겠다.</p>
<h3 id="피어세션">피어세션</h3>
<blockquote>
<p><strong>*&quot;피어세션의 침묵 보안관&quot;*</strong></p>
</blockquote>
<p>이번 주 릴레이 미션 퀘스트가 &#39;피어세션의 침묵 보안관&#39;이었다. 10초 이상 침묵이 흐르지 않도록 해야하는 퀘스트였다. 사실, 퀘스트가 아니더라도 이미 2주차에도 그렇게 했었기 때문에, 나에겐 숨쉬듯이 편안한 퀘스트라고 할 수 있겠다. 특히, 짝 활동이 많았던 이번 주에 정말 많이 느꼈는데, 피어세션 때 조원들과 많이 친해지면 짝으로 진행하는 활동들도 정말 수월해지고, 피어세션에서 피드백의 퀄리티도 달라진다는 것을 많이 느꼈다. 또한, 조원들이 이런 내 노력을 정말 높게 평가 해주어서 굉장히 뿌듯했다. 굳이 퀘스트가 아니더라도 다음주에 한번 더 침묵 보안관이 되어야겠다.</p>
<p><img src="https://velog.velcdn.com/images/jiiker_/post/390ffb99-4eaf-4e42-8c9a-9640293c2900/image.PNG" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jiiker_/post/5f12ce46-324c-4cf5-9abf-20c70b3a2cce/image.PNG" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jiiker_/post/ac434c34-0439-4b5e-901b-eadb8898fe7b/image.PNG" alt=""></p>
<h2 id="3주차-회고">3주차 회고</h2>
<p>이번 주가 정말 배운 것도 많고, 느낀 점도 가장 많은 한 주였던 것 같다!! 하나씩 회고하며 살펴보도록 하자.</p>
<h3 id="keep-유지할-것">Keep (유지할 것)</h3>
<ul>
<li><p><strong>😀즐거운 피어세션 만들기.</strong> 이 부분은 위에서 말했듯이 정말 긍정적인 영향이 많다. 그리고 피어세션이 즐거우면 그만큼 오전 활동에 대한 부담감도 줄어든다는 큰 장점 또한 있다! 챌린지를 진행하며 많은 분들이 본인이 알고 있는 지식을 공유하거나 유익한 자료를 공유하기도 하고, 코딩할 때 듣기 좋은 노래를 공유하기도 하면서 많은 캠퍼들에게 여러 방면에서 서로 도움을 주고 있다. 나는 피어세션이나 릴레이 미션 때 좀 더 재미있는 활동이 될 수 있도록 하는 것으로 도움을 줘야겠다고 생각했다!!</p>
</li>
<li><p><strong>😎새로운 것에 도전하기.</strong> 내가 할 수 있는 것보다 못 하는 것들에 집중해 보자. 이번에 처음으로 클래스 다이어그램을 그리며 설계를 진행 했었는데, 확실히 설계가 좀 더 명확해지고 설계 과정을 다른 사람에게 설명하는 데에도 도움이 많이 됐다. 못 한다고 해서 안 하면 평생 못 할 뿐이다. 새로운 것들을 하나하나 도전하며 챌린지에서 최대한 많은 것을 얻어갈 수 있도록 해보자!</p>
</li>
<li><p><strong>🙋‍♂️다른 캠퍼들과 적극적으로 의견 공유하기.</strong> 이전의 회고에서 계속해서 다음주엔 꼭 질문을 하자고 회고를 했었는데, 질문이 억지로 만들려고 한다고 해서 만들어지는 것은 아니었다. 그래서 조금 방향성을 바꿔 보았다. 질문에 너무 집착하기보다는 다른 캠퍼들과의 의견 공유를 적극적으로 해보려고 했다. 질의응답 세션에서 다른 사람의 질문에 답글을 달아 토론에 참여해보았는데, 이 과정에서 내가 고민하던 것도 해결이 되었고 과제를 좀 더 명확히 분석할 수 있었다. 다음주도 적극적으로 의견 공유에 동참해 보자!</p>
</li>
</ul>
<h3 id="problem-문제점">PROBLEM (문제점)</h3>
<ul>
<li><strong>❌편하고 익숙한 것을 따라가는 것.</strong> 이번 짝 활동들을 진행하고 나서, 다시 돌아보니 너무 내가 편한대로 과제를 해석해서 진행했던 것 같다는 생각이 들었다. 물론 과제를 진행하는 데에 있어 여러 가지 방법들이 있을 수는 있겠지만, 최대한 과제의 의도를 생각해보고 그에 맞게 진행해보려고 노력 해보자!! 챌린지를 챌린지답게!!</li>
</ul>
<h3 id="try-새롭게-시도할-것">TRY (새롭게 시도할 것)</h3>
<ul>
<li><strong>✅구현 단계에서 단위 테스트 진행하기.</strong> 클래스 다이어그램 설계와 더불어 계속해서 외면해오던 것이 테스트 코드 작성이다. 과제를 수행하기에 시간이 부족하다는 이유로 외면해왔다. 이번 주 릴레이 미션 때 읽었던 &#39;소프트웨어 장인&#39;의 6장 &#39;동작하는 소프트웨어&#39; 세션에서는 많은 개발자들이 &#39;시간이 부족하다&#39;는 이유로 테스트 코드를 작성하지 않는 문제를 얘기하면서, 이로 인해 어떤 결과를 초래할 수 있는지 보여주었다. 시간이 좀 걸리더라도 테스트 코드 작성하는 것에 익숙해져 보자!!<blockquote>
<p><strong>*&quot;나에게 나무를 자를 8시간을 준다면, 나는 먼저 6시간을 도끼를 날카롭게 하는 데에 쓰겠다.&quot; -링컨-*</strong></p>
</blockquote>
</li>
</ul>
<h2 id="회고를-마치며">회고를 마치며...</h2>
<p>이번 주에는 무슨 바람이 불었는지, 갑자기 디아블로2가 너무 하고 싶었다. 금요일 활동이 다 끝나자마자 설치하고, 1레벨 원소술사를 만들었다. 그리고 정신을 차려보니...</p>
<p><img src="https://velog.velcdn.com/images/jiiker_/post/157f9403-3e54-4889-a4b9-6e8b9fefadb9/image.PNG" alt=""></p>
<p>지옥의 악마들을 다 소탕해버렸다. 이 때문에 회고가 조금 늦어졌지만, 다시 정신을 가다듬고 챌린지 마지막 4주차를 힘차게 시작해보자!!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[웹 풀스택] 네이버 부스트캠프 챌린지 2주차 회고]]></title>
            <link>https://velog.io/@jiiker_/%EB%84%A4%EC%9D%B4%EB%B2%84-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BA%A0%ED%94%84-%EC%B1%8C%EB%A6%B0%EC%A7%80-2%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@jiiker_/%EB%84%A4%EC%9D%B4%EB%B2%84-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BA%A0%ED%94%84-%EC%B1%8C%EB%A6%B0%EC%A7%80-2%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sun, 28 Jul 2024 06:48:01 GMT</pubDate>
            <description><![CDATA[<h2 id="2주차-생활">2주차 생활</h2>
<p>전반적으로 1주차 보다는 조금 안정된(?) 생활을 했다. 최대한 4시 전에는 침대에 누우려고 했고, 9시 전에 일어나서 피어세션을 준비하는 것을 목표로 했고, 대부분 비슷하게 지켜졌다. 물론 전체적인 수면 시간이 부족하다 보니 상시 피곤한 상태이긴 했지만, 4시간 이상만 수면을 취하면 생각보다는 상태가 괜찮았다.</p>
<h3 id="피어-세션">피어 세션</h3>
<p>이번주는 좀 더 활기찬 피어 세션을 보내기로 다짐했었기 때문에, 줌에 일찍 접속해서 다른 일찍 오신 분들과 인사도 나누고 스몰토크도 했다. 2주차 조원 중 한 분께서 굉장히 친화력 좋으신 분이 계셔서 첫 날부터 많은 얘기를 나누었던 것 같다. 피어 세션에는 매일 진행자를 뽑아서 진행을 맡게 되는데, 하고 싶으신 분이 안계시면 내가 하려고 했지만 그 분께서 진행을 맡아주셨다. 진행 방식부터 발언 시간 체크까지 굉장히 체계적으로 진행을 해주셔서 한 주간 되게 편하게 피어 세션을 진행했던 것 같다. </p>
<h3 id="개인-과제">개인 과제</h3>
<p>개인적으로 1주차에 비해서 조금 익숙한 CS 지식이 나왔던 것 같다. 하지만 주제 자체가 개념적인 내용이라 이를 어떻게 적용해서 구현해야할 지 난감했다. 물론 CS 지식을 너무 피상적으로 공부했었던 탓도 있었던 것 같다. 그래서 이번 주는 구현에 몰입하기 보다는 좀 더 CS 지식을 탄탄하게 다지는 것을 목표로 잡았던 것 같다. 그래서 최대한 학습 정리를 하고 설계 및 구현을 진행하려고 했다. 하지만, 역시 시간에 쫓기게 되면 제일 먼저 포기하게 되는 것은 학습 정리였다... 아무래도 과제를 제출할 때 빈 파일을 제출하는 것은 스스로 용납할 수 없었기 때문인 것 같다.</p>
<p>이 부분은 아직도 뭐가 더 중요한지 판단하기 어려운 것 같다. 학습 정리를 통해 개념을 완벽히 이해하는 것이 중요한 것인지, 직접 구현을 해보고 부딪히는 경험을 하는 것이 중요한 것인지 경중을 판단하는 것이 어렵다. 학습 정리를 완벽히 함으로써 설계 및 구현이 쉬워지는 부분도 있지만, 직접 구현을 하다보면 개념이 더욱 잘 이해되는 경우도 있다. 이 부분은 일단 둘 중에 더 잘 되는 것을 우선하기로 했다. 구현이 잘 안되면 좀 더 학습 정리를, 학습 정리가 잘 안되면 일단 구현을 해보는 방식으로 아등바등 살아봐야겠다.</p>
<h3 id="짝-활동">짝 활동</h3>
<p>주차가 넘어갈수록 짝으로 진행하는 활동이 많아지고, 3주차와 4주차에는 거의 대부분이 짝 활동으로 이루어져 있다. 확실히 개인 과제에 비해서 훨씬 신경써야 할 부분들이 많다. 개인 과제야 뭐 쉬고 싶을 때 쉬고, 하고 싶을 때 하면 되는 것이지만, 짝 활동은 그렇지가 않다. 내가 쉬고 싶다고 마냥 쉴수도, 과제를 하고 싶다고 마냥 진행할 수도 없다. 기본적으로 서로 배려하며 진행되어야 하는 과제이고, 그 부분을 잘 조율하는 것 또한 과제의 일부라고 생각했다. 이런 부분에 있어서는 상대방의 의견을 적극적으로 물어보고, 내 의견 또한 적극적으로 어필하면서 그 타협점을 빠르게 찾는 것이 좋은 것 같다. 그래서 진행 초반에 어떤 방식으로 진행할지(학습 시간, 설계와 구현), 휴식 시간 등을 대략적으로 빠르게 정하고, 중간중간에 상황에 따라 유동적으로 변경해가며 진행했다. 팀원 분께서 짝 활동이 끝나면서 되게 고마워 하셔서 굉장히 뿌듯했다.</p>
<h3 id="릴레이-미션">릴레이 미션</h3>
<p>금요일은 챌린지 과정의 오아시스 릴레이 미션이 있는 날이다. 한 주 동안 피폐해졌던 몸과 마음을 달랠 수 있는 세션이다. 굉장히 가벼운 활동들로 구성되어 있고 마음 편하게 할 수 있어서 나름 힐링(?)할 수 있는 날이다. 조별로 나누어 퀘스트를 만들고, 다른 조에게 넘겨주어 퀘스트를 수행하도록 하는 방식이다. 가끔 하기 어려워 보이는 퀘스트들도 있지만, 대부분 릴레이 미션의 취지에 맞게 재밌는 퀘스트들이 많이 보였다. 기억에 남는 퀘스트는 &#39;어제 실수했던 일 피어세션에서 원영적 사고로 말하기(럭키비키자나 까지 말하면 best)&#39;라는 퀘스트가 재밌어서 기억에 남았다. 이번주에 내가 수행해야할 미션은 &#39;피어세션의 침묵 보안관&#39;이다. 피어세션에서 침묵이 10초 이상 유지되지 않도록 해야한다. 굉장히 재밌는 한 주가 될 것 같다...</p>
<h2 id="2주차-회고">2주차 회고</h2>
<p>저번주 회고에 이것저것 많이 적어놓았지만, 이번주에도 유사한 부분에서 문제를 겪었던 것 같다. 전반적으로는 저번주와 뭔가 비슷한 느낌의 회고가 될 것 같아서 가장 해당하는 것 한 가지씩만 뽑아서 회고를 해보자. 그리고 다음주에는 꼭 개선해볼 수 있도록 하자!!</p>
<h3 id="keep-유지할-것">Keep (유지할 것)</h3>
<ul>
<li><strong>조원들과 친해지기.</strong> 확실히 피어 세션에서 활발하게 얘기를 하다보면 좋은점이 조원들과 빠르게 친해지고, 친해지게 되면 의견을 나누는 게 훨씬 편해진다. 또한 짝 활동을 진행함에 있어서도 부담이 훨씬 줄어들기 때문에 도움이 많이 된다. 특히 다음주는 모든 과제가 짝을 이루어 진행하기 때문에 더욱 필요한 부분일 것 같다.</li>
</ul>
<h3 id="problem-문제점">PROBLEM (문제점)</h3>
<ul>
<li><strong>질문하기.</strong> 이번주에는 질문을 잘 해보리라 다짐했었는데, 결국 질문을 하지 못했다. 질문하려고 한 내용에 대해 이미 토론이 이루어진 뒤라 그 내용을 토대로 나 또한 해답을 얻었기 때문이기도 했다. 하지만 그게 아니었더라도 분명 문제점이 있었다. 내가 질문하고 싶은 내용을 정했으면 그것에 관해서 빠르게 찾아보고 내 생각을 정리했어야 했는데, 전반적인 학습 정리를 위한 자료 조사에 초점을 맞추어 진행했다. 그러다 보니 늦게서야 그 부분에 대해서 찾아보기 시작했고, 이미 slack에서 다른 분들끼리 토의한 내용을 바탕으로 조사하여 해결했다.</li>
</ul>
<h3 id="try-새롭게-시도할-것">TRY (새롭게 시도할 것)</h3>
<ul>
<li><strong>진짜진짜진짜 질문하기.</strong> 그렇다고 아무 질문이나 막 던지지는 말자. 질문을 &#39;잘&#39; 하는 것이 중요하다. 궁금증이 생겼을 때 그 부분에 대해 집요하게 파고들어 보자(그렇다고 여기에 너무 매몰되지는 말자!). 그리고 정말 모르겠을 때 나의 현재 상태와 함께 어떤 부분이 어떻게 잘 안되는지 정리한 뒤에 질문을 해보자! 질문을 잘 하는 것이 정말 어렵다는 점을 많이 느낀 한 주였다. <a href="https://www.youtube.com/watch?v=3smc7jbUPiE">리처드 파인만 교수님의 영상</a>을 다시 한 번 보며 어떻게 질문할지 잘 생각해봐야겠다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Window] VirtualBox에서 Ubuntu 가상 환경 구축하기]]></title>
            <link>https://velog.io/@jiiker_/Window-VirtualBox%EC%97%90%EC%84%9C-Ubuntu-%EA%B0%80%EC%83%81-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jiiker_/Window-VirtualBox%EC%97%90%EC%84%9C-Ubuntu-%EA%B0%80%EC%83%81-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 21 Jul 2024 06:42:50 GMT</pubDate>
            <description><![CDATA[<h2 id="가상-환경virtual-environments이란">가상 환경(Virtual Environments)이란?</h2>
<p><strong>가상 환경(Virtual Environment)</strong>은 물리적인 하드웨어와 독립적으로 소프트웨어를 실행할 수 있는 격리된 공간을 말한다. 이러한 가상 환경은 여러 가지 형태로 구현될 수 있으며, 주요한 형태로는 <strong>가상 머신(Virtual Machine, VM)</strong>과 <strong>컨테이너(Container)</strong>가 있다.</p>
<h3 id="가상-머신vm">가상 머신(VM)</h3>
<p><strong>가상 머신</strong>은 물리적인 하드웨어 위에서 독립적으로 운영체제와 애플리케이션을 실행할 수 있도록 하는 소프트웨어다. 가상 머신을 사용하면 하나의 물리적 컴퓨터에서 여러 운영체제를 동시에 실행할 수 있다. 이를 위해 다음과 같은 구성 요소가 필요하다.</p>
<ul>
<li><strong>하이퍼바이저(Hypervisor)</strong>: 가상 머신을 생성하고 관리하는 소프트웨어. 대표적인 하이퍼바이저로는 VMware, VirtualBox, Hyper-V 등이 있음.</li>
<li><strong>게스트 OS(Guest OS)</strong>: 가상 머신 내에서 실행시킬 운영체제. 지금의 경우에는 가상 환경에서 우분투 운영체제가 게스트 OS에 해당한다.</li>
</ul>
<h3 id="컨테이너container">컨테이너(Container)</h3>
<p><strong>컨테이너</strong>는 가상 머신과 유사하지만, 보다 <strong>경량화</strong>된 가상 환경이다. 컨테이너는 운영체제의 커널을 공유하면서 격리된 공간에서 애플리케이션을 실행할 수 있도록 한다. 대표적인 컨테이너 기술로는 Docker가 있다. 컨테이너는 가상 머신보다 빠르고 효율적이며, 다음과 같은 특징을 가지고 있다.</p>
<ul>
<li><strong>이미지(Image)</strong>: 컨테이너는 이미지를 기반으로 생성. 이미지는 애플리케이션과 그 실행에 필요한 모든 종속성을 포함한다.</li>
<li><strong>컨테이너 엔진(Container Engine)</strong>: 컨테이너를 생성하고 관리하는 소프트웨어. 대표적으로는 Docker가 있다.</li>
</ul>
<h3 id="가상-환경을-왜-사용하는가">가상 환경을 왜 사용하는가?</h3>
<p><strong>하나의 물리적 하드웨어에서</strong> 가상 머신을 통해 <strong>다양한 운영체제</strong>를 <strong>서로 독립된 환경에서 실행</strong>할 수 있어 충돌 없이 테스트 및 개발이 가능하다. <strong>크로스브라우징 테스트</strong>와 같이 다양한 웹 브라우저와 운영체제에서 일관되게 작동하는지 확인하는 데에 매우 유용하다.</p>
<h2 id="윈도우에서-virtualbox를-이용하여-ubuntu-환경-구축하기">윈도우에서 VirtualBox를 이용하여 Ubuntu 환경 구축하기</h2>
<h3 id="virtualbox-설치">VirtualBox 설치</h3>
<blockquote>
<p><a href="https://www.virtualbox.org/wiki/Downloads">https://www.virtualbox.org/wiki/Downloads</a></p>
</blockquote>
<p>위 링크에서 자신의 운영체제에 맞게 설치해주면 된다.</p>
<h3 id="ubuntu-다운로드">Ubuntu 다운로드</h3>
<blockquote>
<p><a href="https://ubuntu.com/download/server">https://ubuntu.com/download/server</a></p>
</blockquote>
<p>위 링크에서 ISO 파일을 다운 받아주면 된다. Ubuntu Desktop도 있고, Ubuntu Server도 있는데 아무거나 다운 받아도 상관 없고, 둘의 차이는 GUI가 있냐 없냐 정도이다. Server의 경우 GUI가 없기 때문에 용량도 적고, 하드웨어 자원이 GUI에 쓰이지 않기 때문에 일을 더 많이 할 수 있다는 장점이 있는 것 같다. 하지만 나의 경우에는 GUI가 있는 게 편할 것 같아서 Desktop을 받았다.</p>
<h3 id="virtualbox-실행-및-ubuntu-환경-구축">VirtualBox 실행 및 Ubuntu 환경 구축</h3>
<ol>
<li><p><strong>VirtualBox 실행</strong></p>
<ul>
<li>설치 후에 실행하면 다음과 같은 UI가 나오는데 <strong>&#39;새로 만들기(N)&#39;</strong>를 눌러 설정을 시작해보자.<image src="https://velog.velcdn.com/images/jiiker_/post/1379ae6f-d4f3-4880-89e7-07541106ba29/image.png" width="80%"/>
</li>
</ul>
</li>
<li><p><strong>가상 머신 이름과 운영 체제</strong></p>
<ul>
<li>이름은 아무렇게나 지어주고, ISO 이미지는 아까 다운받아뒀던 ISO 파일을 선택해주자.<image src="https://velog.velcdn.com/images/jiiker_/post/81866920-db91-4b39-ac02-6eb590dcf156/image.png" width="90%"/></li>
<li>트러블 슈팅 과정에서 ISO 파일 버전의 문제인가 싶어 여러 개를 다운 받아서 테스트 해봤는데, 결과적으로 내가 겪은 트러블과 버전은 상관이 없었다. 아무거나 선택해주자!<image src="https://velog.velcdn.com/images/jiiker_/post/b3c788b6-23af-45e9-a5d6-fb930b80678a/image.PNG" width="90%"/>


</li>
</ul>
</li>
</ol>
<ol start="3">
<li><p><strong>무인 게스트 OS 설치</strong></p>
<ul>
<li>여기서 사용자 <strong>이름</strong>과 <strong>암호</strong>를 설정 해주자.</li>
<li>아래에 <strong>&#39;게스트 확장(E)&#39;</strong> 이라는 기능이 있는데, 나중에 게스트와 호스트 간에 <strong>클립보드를 공유</strong>한다거나, <strong>드래그 앤 드롭</strong>으로 파일을 복사할 수 있게 해준다거나 유용한 기능들을 위해 필요한 것이니 체크해두자!<image src="https://velog.velcdn.com/images/jiiker_/post/0bf7b796-0072-488f-8896-a6a57d2ab0bb/image.PNG" width="90%"/>
</li>
</ul>
</li>
<li><p><strong>하드웨어</strong></p>
<ul>
<li><strong>&#39;다음&#39;</strong>
<img src="https://velog.velcdn.com/images/jiiker_/post/91d67ff2-1d7f-4d4a-bc2f-adca83d44c00/image.PNG" alt=""></li>
</ul>
</li>
</ol>
<ol start="5">
<li><strong>가상 하드 디스크</strong><ul>
<li><strong>&#39;다음&#39;</strong>
<img src="https://velog.velcdn.com/images/jiiker_/post/a0207f6a-de5d-4a55-93b4-e1503b329c78/image.PNG" alt=""></li>
</ul>
</li>
</ol>
<ol start="6">
<li><strong>Ubuntu 실행</strong><ul>
<li>설정을 완료하면 자동 실행이 되지만, 만약 자동으로 실행되지 않는다면 초기 화면에서 내가 만든 운영체제를 선택한 후 <strong>&#39;시작(T)&#39;</strong> 버튼을 눌러주자.
<img src="https://velog.velcdn.com/images/jiiker_/post/da235fa6-01fd-41db-ab48-8d96d92fcf7d/image.png" alt="">   </li>
<li>그러면 쌍팔년도스러운 화면과 함께 시작이...
<img src="https://velog.velcdn.com/images/jiiker_/post/577dddd3-8226-4eb5-a223-d9d80a9a83ed/image.PNG" alt=""></li>
<li>되어야 하는데...</li>
<li>혹시 안 되는 사람 있나요...?</li>
<li>처음 VirtualBox를 설치하고 Ubuntu를 다운받아 실행했을 때 아래와 같은 화면에서 더 이상 진행이 되지 않았다.
<img src="https://velog.velcdn.com/images/jiiker_/post/45367083-862e-4594-a490-3c8922d73d2a/image.PNG" alt=""></li>
<li>지금부터 다룰 내용은 여기서 정상적으로 실행되지 않은 사람들을 위한 나의 트러블슈팅을 정리한 내용이다.  </li>
</ul>
</li>
</ol>
<h3 id="window-가상화-설정-문제">Window 가상화 설정 문제</h3>
<blockquote>
<p>&quot;Not in a hypervisor partition (HVP=0) (VERR_NEM_NOT_AVAILABLE).&quot;
&quot;AMD-V is disabled in the BIOS (or by the host OS) (VERR_SVM_DISABLED).&quot;</p>
</blockquote>
<p>지금 글을 쓰면서 에러 메시지를 타이핑 했는데, 에러메시지가 굉장히 친절했었구나... 하는 생각이 든다. 왜 이걸 헤맸을까... 이를 번역하자면 이런 의미이다.</p>
<ul>
<li>하이퍼바이저 파티션에 있지 않음.(가상화 관리 소프트웨어 내에서 실행되고 있지 않다는 뜻)</li>
<li>BIOS에서 AMD-V가 비활성화 되어있음.</li>
</ul>
<p>따라서 해결은 가상화 관리 소프트웨어 내에서 실행될 수 있도록 <strong>BIOS에서 AMD-V 설정을 활성화</strong> 시켜주면 된다!</p>
<ol>
<li><strong>가상화 설정 확인</strong><ul>
<li><strong>&#39;작업 관리자&#39;</strong>를 실행시켜 <strong>&#39;성능&#39;</strong>탭에서 확인할 수 있다.</li>
<li>현재 상태는 <strong>&#39;사용 안함&#39;</strong>이었기 때문에 위와 같은 에러가 발생했던 것이다.<image src="https://velog.velcdn.com/images/jiiker_/post/6d1b4d24-1b78-4a25-975b-baf1f9c8b324/image.png" width="70%"/>


</li>
</ul>
</li>
</ol>
<ol start="2">
<li><p><strong>BIOS 모드 접근 방법</strong></p>
<ul>
<li><p><strong>BIOS 모드로 부팅</strong>하려면, 컴퓨터를 시작할 때 특정 키를 눌러서 BIOS 설정에 들어가야 한다. 일반적으로 사용하는 키는 <strong>제조사에 따라 다를 수 있다</strong>.</p>
<table>
<thead>
<tr>
<th>제조사</th>
<th>BIOS 설정 키</th>
</tr>
</thead>
<tbody><tr>
<td>Dell</td>
<td>F2 또는 Del</td>
</tr>
<tr>
<td>HP</td>
<td>F10 또는 Esc</td>
</tr>
<tr>
<td>Lenovo</td>
<td>F2 또는 Fn + F2</td>
</tr>
<tr>
<td>ASUS</td>
<td>Del 또는 F2</td>
</tr>
<tr>
<td>Acer</td>
<td>Del 또는 F2</td>
</tr>
<tr>
<td>MSI</td>
<td>Del</td>
</tr>
<tr>
<td>Gigabyte</td>
<td>Del 또는 F2</td>
</tr>
<tr>
<td>Samsung</td>
<td>F2</td>
</tr>
<tr>
<td>Toshiba</td>
<td>F2 또는 Esc</td>
</tr>
</tbody></table>
<ul>
<li><strong>윈도우</strong>의 경우 <strong>Shift 키를 누른 상태</strong>로 <strong>다시 시작</strong>을 클릭하는 방법으로 BIOS 설정에 들어갈 수도 있다.</li>
</ul>
</li>
</ul>
</li>
</ol>
<ol start="3">
<li><p><strong>BIOS에서 설정 방법</strong></p>
<ul>
<li><p>이 부분은 어떤 CPU를 사용했는지에 따라 조금 달라질 수도 있고, 잘 정리된 글이 있어서 해당 <strong>링크를 첨부</strong>하도록 하겠다.</p>
<blockquote>
<p>&lt; 참고 링크 : <a href="https://fivem.tistory.com/24">https://fivem.tistory.com/24</a> &gt;</p>
<p>1) BIOS에서 <strong>&quot;Advanced(F7)&quot; 메뉴</strong>로 이동</p>
<p>2) <strong>Advanced 탭</strong>을 선택한 뒤 <strong>&quot;CPU Configuration&quot;</strong> 혹은 <strong>&quot;Chipset Configuration&quot;</strong>을 찾아 이동</p>
<p>3) <strong>&quot;SVM Mode&quot;</strong> 또는 <strong>&quot;AMD Virtualization&quot;</strong>을 찾아 <strong>Enable</strong>로 설정 
(인텔 CPU의 경우 &quot;Intel Virtualization Technology&quot; 또는 &quot;VT-x&quot; 또는 &quot;Intel 가상화 기술&quot;)</p>
<p align="center"><image src="https://velog.velcdn.com/images/jiiker_/post/6fe459f6-a9aa-4e96-927f-3bbc5efdf764/image.jpg" width="70%"/></p>

<p>4) 설정을 <strong>저장</strong> 후 <strong>나가기</strong></p>
</blockquote>
</li>
</ul>
</li>
<li><p><strong>가상화 설정 재확인</strong></p>
<ul>
<li>이후에 작업관리자를 확인해보면, 가상화 부분이 <strong>&#39;사용&#39;</strong>으로 변경된 것을 확인할 수 있다.
<img src="https://velog.velcdn.com/images/jiiker_/post/875c06aa-41ef-46c5-88e2-290f4cad10a3/image.png" alt=""></li>
</ul>
</li>
<li><p><strong>VirtualBox에서 Ubuntu 실행</strong></p>
<ul>
<li>다음 화면과 같이 정상 실행되는 것을 확인할 수 있다!!<image src="https://velog.velcdn.com/images/jiiker_/post/8317a106-34f2-4660-872f-a86580908357/image.PNG" width="70%"/>



</li>
</ul>
</li>
</ol>
<h2 id="회고">회고</h2>
<p>정리하면서 느꼈지만 정말 간단한 에러이고, 에러 메시지도 친절해서 금방 해결했어야 했는데, 그러지 못했다. 처음 마주했을 때는 도대체 어떤 키워드로 검색해야 해결할 수 있을지 몰라서 이것저것 찾다보니 Hyper-V 설정을 하고 있기도 했다. 자료를 조사하면서 Hyper-V 가 무엇인지 알고나니 헛웃음이 나오기도 했다. 하지만 이러면서 또 한 단계 성장하는 것 아니겠는가...!!! 내일은 좀 더 나은 감자가 되어보자...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[웹 풀스택] 네이버 부스트캠프 챌린지 1주차 회고]]></title>
            <link>https://velog.io/@jiiker_/%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BA%A0%ED%94%84-%EC%B1%8C%EB%A6%B0%EC%A7%80-1%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@jiiker_/%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BA%A0%ED%94%84-%EC%B1%8C%EB%A6%B0%EC%A7%80-1%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sat, 20 Jul 2024 14:36:24 GMT</pubDate>
            <description><![CDATA[<h2 id="챌린지-과정은">챌린지 과정은?</h2>
<p>부스트캠프 챌린지 과정은 4주 동안 <strong>CS 관련 지식을 습득</strong>하고 <strong>관련 기능을 직접 구현</strong>해보는 과정으로 이루어져 있다. CS 지식에 미흡한 부분이 많다고 느꼈던 나로써는 이 부분을 채워나갈 수 있는 정말 좋은 기회라고 생각이 되었다. 하지만 부족한만큼 챌린지의 난이도는 상승하게 된다.(<del>여긴 지옥이야..</del>) 거의 매일 밤을 새웠고, 남들은 쉽게 해결하는 부분에서 막혀서 오랜 시간을 투자하기도 했다. 그래도 내가 부족한 게 많기 때문에 그만큼 얻어갈 수 있는 것이 많다는 마인드로 열심히 임하고 있다!!!</p>
<h2 id="챌린지-과정에서-만난-사람들">챌린지 과정에서 만난 사람들</h2>
<p>매번 포스팅에서 강조했지만, 늘 좋은 사람들을 만나는 것만큼 성장에 도움이 되는 것이 또 없다고 생각한다.(그래서 또 얘기해보려 한다.) 이전까지 진행했던 라피신이나 구름톤 트레이닝의 경우에는 선발 과정 자체가 선착순이거나 간단한 면접 정도였기 때문에 교육생들의 실력 분포가 다양했던 반면에 부스트캠프 챌린지의 경우에는 한눈에 봐도 잘 하시는 분들의 비율이 매우 커 보였다...(<del>내가 취업이 안되는 이유를 여기서 찾았네...</del>) 첫 주 시작부터 슬랙에 올라오는 갖은 전문 용어들에 조금은 기가 죽었던 것 같기도 하다.</p>
<p>아직 1주차가 막 마무리 된 것 뿐이어서 엄청 친해졌다거나 하는 사람은 없지만, 다들 새로운 시스템에 적응하느라 바빴기 때문일 것 같다. 일단 과제 해결을 위한 자료 조사나 구현을 위해 시간이 정말 많이 쓰이기도 하지만, 중간중간에 여러 제출 기한이 정해져 있기 때문에 그런 루틴도 익숙해져야 했다. 그리고 피어세션(동료 평가)에서 대화할 시간이 생각보다 적기 때문에 피드백 진행에 급급했던 것 같다. 그래도 이제는 좀 익숙해졌으니까 다음주는 좀 더 편하게 진행할 수 있지 않을까 기대해 본다!! 그리고 상시 개방해놓는 라운지 줌 주소가 있는데 다음주에는 라운지에서도 동료를 찾아보러 떠나봐야겠다.(도도독..!)</p>
<p align="center">
    <image src="https://velog.velcdn.com/images/jiiker_/post/58fabfb5-33d0-47f4-877d-380dcd4ea118/image.jpg" width="50%"/>
  <figcaption style="text-align:center; font-size:15px; color:#808080; margin-top:-20px">
    출처: 유튜브 채널 'LE SSERAFIM'
  </figcaption>
</p>

<p>그리고 많은 사람들과 친해지면 좋은 것이 이 좁은 대한민국 땅덩어리 안에서는 언젠가 또 만나게 된다. 챌린지 과정에서 이전 과정들에서 만났던 분들을 두 분이나 다시 만났다!! 마치 먼 타국에서 한국인을 만난 느낌이 이런 느낌인가?? 그 당시에 얼굴만 아는 정도였지만 이 곳에서 만나니 너무 반가웠다!! 서로의 힘듦을 공유할 수 있어서 너무나도 위로가 되었다!! 앞으로 다 같이 화이팅 합시다!!!~</p>
<h2 id="진짜-1주차-회고">진짜 1주차 회고..</h2>
<p>어떤 방식으로 회고를 할 지 고민해봤는데, 역시나 KPT 방식이 심플하면서 좋은 것 같다!! 1주차를 보내면서 했던 것 중 <strong>유지할 것(Keep), 문제점(Problem), 새롭게 시도할 것(Try)</strong>로 나누어 살펴보자.</p>
<h3 id="keep-유지할-것">KEEP (유지할 것)</h3>
<ul>
<li><p><strong>문제를 혼자 힘으로 해결하기 위해 노력한 것.</strong> 정말 낯선 문제가 나와도 어떻게든 요구사항을 분석하고 해결하기 위해 어떤 것들이 필요한지 찾아내는 그 과정 자체가 정말 의미 있었다고 생각한다.</p>
</li>
<li><p><strong>미친 텐션.</strong> 개인적으로 개발팀에 의무적으로 ENFP 개발자를 한 명씩 배치해야 한다고 생각한다.(<del>진심입니다.</del>) 사실 이번주는 조금 부족했나 싶지만... 피어세션에서 틈틈히 스몰토크를 시도했었는데 이제 친해지려고 하니 조가 바뀌어서 너무 아쉬울 따름이다... 반대로 구현 미션이 없는 금요일은 너무 행복해서 텐션이 너무 올라갔었는데, 릴레이 미션 조원 분이 조금 부담스러워 하셨던 것 같다...</p>
</li>
</ul>
<h3 id="problem-문제점">PROBLEM (문제점)</h3>
<ul>
<li><p><strong>컨디션 관리.</strong> 1일차엔 오전 2<del>3시에 잤던 것 같은데, 2일차에 오전 7시 취침, 3일차 오전 4시 취침, 4일차 오전 5시 취침... 그리고 매일 아침 8시</del>9시에 일어났으니 평균 4~5시간 정도 수면을 취했던 것 같다. 이렇게 보니 생각보다 많이 잔 것 같기도 하고..?? 그런데 4일차에 정말 머리가 멍해서 머리가 정말 안 돌아가서 팀 미션 수행 중에 계속 요구사항 디테일을 놓쳐서 팀원 분께서 계속 정정해주셨던 것이 기억난다... 수면시간을 확보하기 위해 좀 더 효율적으로 학습하고 정리하는 방법을 찾아야 할 것 같다.</p>
</li>
<li><p><strong>질문하기.</strong> 이 부분이 기준이 참 애매한데, 질문을 잘 하기 위해서는 어느 정도 찾아보기도 해야하고 고민해본 뒤에 질문을 하는 것이 맞다. 하지만 그렇게 꼬리에 꼬리를 물어 찾아보다보면 결국 대부분은 찾아진다. 1주차에 하나의 이슈가 있었는데, 굉장히 간단한 부분에서 막혔다. 질문하면 어떻게 해요? 라는 1차원 질문밖에 불가능 했을 것 같기도 하고, 일단 자존심의 문제였다. 내가 이것도 못한다고? 라는 생각에 절대로 질문할 수가 없었다... 그 문제를 해결하는 데 5시간 정도가 소요됐고, 결국 이것 때문에 밤을 새게되었다. 질문을 잘 하는 능력도 이번 챌린지에서 키워나갈 핵심 역량으로 삼아야겠다.</p>
</li>
</ul>
<h3 id="try-새롭게-시도할-것">TRY (새롭게 시도할 것)</h3>
<ul>
<li><p><strong>하루에 하나씩 질문을 해보자.</strong> 뭐든 질문을 해봐야겠다. 사실 이로써 위의 두 문제가 모두 해결될 수도 있는 일이다. 사실 질문을 하기 어려웠던 것이 마스터님들의 일침이 무서웠던 것도 있다. 하지만 그 분들은 우리를 해코지하러 오신 게 아니다. 이번 릴레이 미션 때 읽었던 &#39;소프트웨어 장인(로버트 C. 마틴)&#39;이라는 책에서도 말하지만 &#39;나를 가르치는 데 기꺼이 시간을 투자하는 사람&#39;은 정말 흔치 않다. 우리가 올바르게 성장할 수 있도록 돕는 고마운 분들이다! 무서워 하지 말자!(<del>무섭게 생기신 분이 한 분...읍읍..</del>)</p>
</li>
<li><p><strong>여유를 가져보자.</strong> 마음만 급해봐야 학습 효율만 더 떨어질 뿐이다. 다 알고있는 얘기지만 실천이 쉽지않다. 지금 구현을 하나도 못 했는데, 밖에 나가서 산책을 30분이나 하고 온다고? 좀 하고 오자! </p>
</li>
</ul>
<h2 id="회고를-마치며">회고를 마치며...</h2>
<p>1주차를 지내고 부스트캠프가 정말 잘 설계된 교육이라고 느꼈다. &#39;무엇을 어떻게 해라!&#39;라고 명확하게 제시하지는 않지만, 2시까지 과제 해결을 위한 나만의 체크리스트를 제출하고, 7시에는 나의 학습을 체크해볼 수 있는 체크포인트를 제출하게 함으로써 간접적으로 가이드를 잘 해주는 느낌을 많이 받았다. 또한 4일 동안은 <code>while(1) { &quot;정말&quot; }</code> 힘들었지만, 금요일에 책을 읽는 힐링(?) 시간과 주말 동안 충분히 회복 할만한 시간이 있다. 진짜 쉬라고 시간을 준다면 사실 5일 동안 미션하고, 주말에 쉬면 된다. 하지만 이렇게 금요일에 힐링 시간을 굳이 끼워넣은 이유는 금요일 포함 3일 동안 여유롭게 4일동안 복잡해진 머릿속을 정리해보라는 의미가 아닐까 생각해본다. 아직 내일이 일요일이라는 것이 너무 행복하다... </p>
<p>챌린지 하시는 모든 분들 화이팅입니다!!!!!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 1309번] 동물원 - DP]]></title>
            <link>https://velog.io/@jiiker_/%EB%B0%B1%EC%A4%80-1309%EB%B2%88-%EB%8F%99%EB%AC%BC%EC%9B%90-DP</link>
            <guid>https://velog.io/@jiiker_/%EB%B0%B1%EC%A4%80-1309%EB%B2%88-%EB%8F%99%EB%AC%BC%EC%9B%90-DP</guid>
            <pubDate>Sat, 13 Jul 2024 03:57:33 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p>어떤 동물원에 가로로 두칸 세로로 N칸인 아래와 같은 우리가 있다.</p>
<p align="center">
    <image src="https://velog.velcdn.com/images/jiiker_/post/b9784a47-98e1-402a-a6bd-168ed21cc151/image.jfif" width="30%"/>
</p>


<p>이 동물원에는 사자들이 살고 있는데 사자들을 우리에 가둘 때, 가로로도 세로로도 붙어 있게 배치할 수는 없다. 이 동물원 조련사는 사자들의 배치 문제 때문에 골머리를 앓고 있다.</p>
<p>동물원 조련사의 머리가 아프지 않도록 우리가 2*N 배열에 사자를 배치하는 경우의 수가 몇 가지인지를 알아내는 프로그램을 작성해 주도록 하자. 사자를 한 마리도 배치하지 않는 경우도 하나의 경우의 수로 친다고 가정한다.</p>
<hr>
<h2 id="문제-분석">문제 분석</h2>
<ul>
<li><p>2 * N 사이즈의 우리에 사자들 배치</p>
</li>
<li><p>사자들을 <strong>가로로도 세로로도</strong> 붙어 있게 배치할 수는 없다.</p>
</li>
<li><p>배치할 수 있는 경우의 수 구하기</p>
</li>
<li><p>한 마리도 배치하지 않는 경우도 한 가지 경우의 수에 해당</p>
</li>
</ul>
<hr>
<h2 id="접근-방법">접근 방법</h2>
<p>나는 우선 경우의 수 문제를 보면 <strong>완전탐색</strong> 혹은 <strong>DP</strong>를 생각해두는데, 우선 완전탐색을 했을 경우에 시간복잡도를 어림잡아 계산 해보고, 이건 아니다 싶으면 DP를 선택하는 것 같다.</p>
<p>이 문제의 경우는 <strong>N이 10만</strong>일 경우 <strong>20만 개의 칸</strong>이 있고 각각의 칸에 사자를 넣거나 안 넣을 수 있으니 <strong>총 경우의 수가 2의 20만 제곱</strong>이 나오는 것 같아서, <strong>DP</strong>로 풀어야 하는구나 생각했다.</p>
<p>DP 문제는 항상 느끼는 것이 펜을 들게 만드는 것 같다. 마냥 문제만 들여다보고 있으면 규칙이 잘 보이지는 않는 것 같아서 써 보면서 규칙을 찾는 편이다.</p>
<h3 id="손으로-써보기">손으로 써보기</h3>
<p>규칙을 찾으려고 손으로 써 본 것도 있지만 <strong>&#39;가로로도 세로로도&#39;</strong>의 의미가 명확한 것 같지 않아서 써본 것도 있다. 특정 칸을 기준으로 가로와 세로에 동시에 붙어 있으면 안된다는 것인지 둘 중 하나만 붙어 있어도 안된다는 것인지 애매해서 예제 케이스를 손으로 써 보았다.</p>
<p align="center">
    <image src="https://velog.velcdn.com/images/jiiker_/post/b39b9a2d-534a-464e-865d-f51d9b6e113b/image.jpg" width="40%"/>
</p>

<p>처음에 가로와 세로에 동시에 붙어 있으면 안된다는 의미로 계산 해봤더니 경우의 수가 너무 많이 나왔고, <strong>둘 중 하나만 붙어 있어도 안된다는 의미</strong>로 계산 해보니 예제 정답과 똑같이 나왔다. 손으로 써 보면서 규칙을 찾게 되었는데, 이제 저 필기의 의미를 해석해보도록 하자.</p>
<hr>
<h2 id="문제-풀이">문제 풀이</h2>
<h3 id="1-한-줄에-배치할-수-있는-경우의-수">1. 한 줄에 배치할 수 있는 경우의 수</h3>
<p align="center">
    <image src="https://velog.velcdn.com/images/jiiker_/post/3ecbbeb8-0145-44e2-b6ab-08154ba47ecd/image.PNG" width="60%"/>
</p>

<p>한 줄에 사자를 배치할 수 있는 경우는 총 세 가지다. 한 마리도 안 넣거나, 오른쪽에 한 마리 혹은 왼쪽에 한 마리를 배치할 수 있다. 각각의 경우에 1번, 2번, 3번 번호를 붙여두자.</p>
<h3 id="2-다음-줄과의-관계">2. 다음 줄과의 관계</h3>
<ul>
<li><p><strong>1번</strong>
  윗 줄이 1번과 같은 빈 줄이었다면, 다음 줄에 올 수 있는 경우는 <strong>1번, 2번, 3번 세 가지</strong> 경우가 올 수 있다.</p>
</li>
<li><p><strong>2번</strong>
  윗 줄이 2번이었다면, 똑같은 2번과 같은 경우를 제외하고 <strong>1번, 3번 두 가지</strong> 경우가 올 수 있다.  </p>
</li>
<li><p><strong>3번</strong>
  마찬가지로 윗 줄이 3번이었다면, 똑같은 3번과 같은 경우를 제외하고 <strong>1번, 2번 두 가지</strong> 경우가 올 수 있다.</p>
</li>
<li><p><strong>수형도(나뭇가지 그림)</strong>
  위 그림을 수형도를 이용해서 그려보면 아래 그림과 같이 나타낼 수 있고, <strong>맨 아랫줄의 경우를 다 세어주면 총 경우의 수</strong>가 된다.</p>
</li>
</ul>
<p align="center">
    <image src="https://velog.velcdn.com/images/jiiker_/post/23f7ec20-56e8-4877-b08c-49d2fbf2c4d0/image.PNG" width="60%"/>
</p>


<ul>
<li><p><strong>점화식</strong>
  윗 줄의 1번, 2번, 3번의 개수를 알고 있으면 다음 줄의 1번, 2번, 3번의 개수를 알아낼 수 있다.</p>
<ul>
<li><strong>(1번 개수) = (윗 줄의 1번 개수) + (윗 줄의 2번 개수) + (윗 줄의 3번 개수)</strong></li>
<li><strong>(2번 개수) = (윗 줄의 1번 개수) + (윗 줄의 3번 개수)</strong></li>
<li><strong>(3번 개수) = (윗 줄의 1번 개수) + (윗 줄의 2번 개수)</strong></li>
</ul>
<p>와 같이 나타낼 수 있고, 마지막 줄의 1번, 2번, 3번 개수를 다 더하면 총 경우의 수를 구할 수 있다.</p>
</li>
</ul>
<hr>
<h2 id="코드c">코드(C++)</h2>
<pre><code class="language-cpp">#include &lt;bits/stdc++.h&gt;

using namespace std;

int main() {
  int N = 0;
  vector&lt;pair&lt;pair&lt;int, int&gt;, int&gt;&gt; DP;

  cin &gt;&gt; N;

  DP.push_back({{1, 1}, 1});

  for (int i = 0; i &lt; N; i++) {
    int x, y, z;

    x = (DP[i].first.first + DP[i].first.second + DP[i].second) % 9901;
    y = (DP[i].first.first + DP[i].second) % 9901;
    z = (DP[i].first.first + DP[i].first.second) % 9901;

    DP.push_back({{x, y}, z});
  }

  cout &lt;&lt; (DP[N-1].first.first + DP[N-1].first.second + DP[N-1].second) % 9901;

  return 0;
}</code></pre>
<hr>
<h2 id="글을-마치며">글을 마치며</h2>
<p>요즘 자소서나 네부캠이나 너무 정신이 없어서 포스팅할 시간이 없었다고 변명은 해보지만... 길게 쓰진 않더라도 짧은 글이라도 꾸준히 내 생각을 기록하는 습관을 들여봐야겠다!! 다음주부터 네이버 부스트캠프 챌린지 과정에 들어가는데, 챌린지 과정에서 공부하는 것들을 회고하는 방식으로 포스팅 해봐야겠다!!</p>
]]></description>
        </item>
    </channel>
</rss>