<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jin_h2_.log</title>
        <link>https://velog.io/</link>
        <description>티모누나예요🐶</description>
        <lastBuildDate>Tue, 14 Oct 2025 07:25:38 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jin_h2_.log</title>
            <url>https://velog.velcdn.com/images/jin_h2_/profile/1cb20bf3-3e3f-466a-9045-6ac6e0d3f2f7/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jin_h2_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jin_h2_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[JMeter]]></title>
            <link>https://velog.io/@jin_h2_/JMeter</link>
            <guid>https://velog.io/@jin_h2_/JMeter</guid>
            <pubDate>Tue, 14 Oct 2025 07:25:38 GMT</pubDate>
            <description><![CDATA[<p>내가 보려고 만든 성능테스트</p>
<h2 id="설치-및-실행">설치 및 실행</h2>
<p>[ 설치 링크 ]
<a href="https://jmeter.apache.org/download_jmeter.cgi">https://jmeter.apache.org/download_jmeter.cgi</a></p>
<p>binary의 zip 파일을 설치하고 bin 폴더 내 jmeter.bat 파일 실행</p>
<h2 id="기본-설정">기본 설정</h2>
<h3 id="1-사용자-그룹-추가">1. 사용자 그룹 추가</h3>
<p>** 👉 Test Plan - Add - Threads - Treads Group **</p>
<ul>
<li>Number of Treads : 쓰레드 수(테스트 할 유저 수)</li>
<li>Ramp-up period : 쓰레드 수를 실행시키는 데 걸리는 시간</li>
<li>Loop Count : 요청 반복 횟수</li>
</ul>
<p>예를 들어 100명 / 10초로 설정했을 경우, 초당 10명을 추가하여 부하를 준다.</p>
<p><img src="https://velog.velcdn.com/images/jin_h2_/post/34e5acef-7e18-471d-a3cf-7d1d1bdda7d0/image.png" alt=""></p>
<h3 id="2-http-기본-요청값-설정">2. HTTP 기본 요청값 설정</h3>
<p>하위 HTTP Request 샘플러에 대한 기본 설정값 추가.
<strong>👉 Test Plan - Add - Config Element - HTTP Request Default</strong>
<img src="https://velog.velcdn.com/images/jin_h2_/post/c7d9be3f-e89b-482e-9625-217040d3bb05/image.png" alt=""></p>
<h3 id="3-결과-확인-메뉴-listener">3. 결과 확인 메뉴 (Listener)</h3>
<p><strong>👉 Test Plan - Add - Listener - View Results Tree / Summary Report / Aggregate Report</strong></p>
<h4 id="1-view-results-tree">1) View Results Tree</h4>
<p>요청별 결과 상세 정보를 확인.</p>
<h4 id="2-summary-report">2) Summary Report</h4>
<p>요청에 대한 결과가 요약된 보고서.</p>
<pre><code>- #Samples
요청 수.

- Average
전체 요청의 응답시간을 합산하고
이를 요청 수로 나눈 평균값

- Min / Max
최소 / 최대 응답 시간
Max가 크다면 확인 필요

- Std. Dev.
표준편차로 높을수록 성능이 불안전함을 의미

- Error %
에러발생율
View Results Tree에서 에러 확인 가능

- Throughput
초당 처리량(TPS)

- Received / Sent KB
서버로 초당 수신 / 송신된 데이터

- Avg. Bytes
응답 데이터의 평균 크기
너무 크다면 불필요한 데이터가 있는지 확인</code></pre><h4 id="3-aggregate-report">3) Aggregate Report</h4>
<p>응답시간의 분포를 확인할 수 있는 집계 보고서
하단 항목 외의 항목들은 요약보고서와 동일.</p>
<pre><code>- Median
응답시간 중앙값으로 평균적인 성능 평가
응답 시간이 흩어져 있으면 Std. Dev.가 높게 나옴

- 90% / 95% / 99% Line
요청의 n%가 해당 시간 안으로 응답하고, 나머지는 초과하여 응답
즉, 높을수록 좋지 않음</code></pre><h3 id="4-시나리오-생성">4. 시나리오 생성</h3>
<p> 로그인을 예시로 시나리오 생성 시작</p>
<h4 id="ex-로그인-csv-사용">ex) 로그인 (csv 사용)</h4>
<p> Excel에서 유저의 아이디와 비밀번호를 입력하고 csv 파일로 저장한다.
 여기서는 1행에 변수명을 작성해 주었다.
 <img src="https://velog.velcdn.com/images/jin_h2_/post/6d9617d2-3e9a-487c-9f74-e8c268277951/image.png" alt=""></p>
<p><strong>👉 Test Plan - Add - Config Element - CSV Data Set Config</strong>
Filename에 csv 파일을 등록하고
Variable Names에 변수명을 입력한다. (빈칸 주의)
앞서 1행에 데이터값이 아닌 변수명을 적어주었기에 Ignore first line을 True로 설정한다.</p>
<p><img src="https://velog.velcdn.com/images/jin_h2_/post/962f7c39-7dac-4e84-95a0-e685516571e0/image.png" alt=""></p>
<p><strong>👉 Treads Group - Add - Sampler - HTTP Request</strong>
변수명을 사용하고자 한다면 <code>${변수명}</code>으로 사용이 가능하다.
하단의 탭에서 요청 방법에 따라 parameter, body, header에 값을 넣어준다.
parameter에서 데이터를 추가할 땐 하단의 버튼(Add)를 눌러 추가가 가능하다.</p>
<p><img src="https://velog.velcdn.com/images/jin_h2_/post/5df37d8f-b458-4577-9cb9-a64e0d63c890/image.png" alt=""></p>
<blockquote>
<p><strong>쿠키에 토큰을 저장</strong>
👉 Treads Group - Add - Config Element - HTTP Cookie Manager
로그인 성공시 자동으로 쿠키가 파싱된다.
별도로 추가가 필요하다면 하단 Add 버튼을 눌러 추가할 것.</p>
</blockquote>
<blockquote>
<p><strong>LocalStorage에 토큰을 저장?</strong>
 👉 Treads Group - Add - Config Element - HTTP Header Manager
요청 헤더에 넣어줄 값을 하단 Add 버튼을 눌러 추가해준다.
<img src="https://velog.velcdn.com/images/jin_h2_/post/716b2a88-c76d-45f4-886f-a67631870592/image.png" alt="">
👉 로그인 Request - Add - Post Processors - Regular Expression Extractor
아래와 같이 작성하여 로그인 응답 헤더에서 토큰을 추출하고 모든 요청에 토큰값을 넣을 수 있도록 한다.
위에서 사용된 변수명과 동일하게 넣어줄 것.
<img src="https://velog.velcdn.com/images/jin_h2_/post/3dbdcf73-a47b-4e41-ab1a-18a1e3c91b1d/image.png" alt=""></p>
</blockquote>
<p>마지막으로 초록색의 Start 버튼을 눌러 실행하고 결과를 확인하면 끝.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[폰트 깜빡임 - Font Preload]]></title>
            <link>https://velog.io/@jin_h2_/%ED%8F%B0%ED%8A%B8-%EA%B9%9C%EB%B9%A1%EC%9E%84-Font-Preload</link>
            <guid>https://velog.io/@jin_h2_/%ED%8F%B0%ED%8A%B8-%EA%B9%9C%EB%B9%A1%EC%9E%84-Font-Preload</guid>
            <pubDate>Wed, 20 Nov 2024 07:08:07 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-발생">문제 발생</h2>
<p>GNB에 마우스 커서를 올렸을 때 LNB가 나타나는데,
이 때 기본 폰트가 보였다가 설정한 웹폰트로 변하게 됩니다.</p>
<h2 id="원인">원인</h2>
<p>브라우저의 렌더링 프로세스는 아래와 같습니다.</p>
<blockquote>
</blockquote>
<ol>
<li>HTML 파싱, DOM 생성</li>
<li>CSS 파싱, CSSOM 생성</li>
<li>렌더 트리 생성</li>
<li>Reflow (레이아웃 계산)</li>
<li>페인팅</li>
</ol>
<p>LNB는 <code>display:none</code>으로 되어 있어 렌더 트리에 포함되지 않아
LNB가 보이는 상태가 되어서야 웹폰트를 로딩하게 됩니다.</p>
<p>그런데 사용하고자 하는 웹폰트는 확장자가 <code>OTF</code>로,
이는 폰트 로딩 순서 중에서 가장 느린 확장자이며
실제로 로드까지 100ms 이상의 시간이 걸리는 것을 확인했습니다.</p>
<blockquote>
<p>폰트 로딩 순서
woff2 &gt; woff &gt; ttf &gt; eot &gt; svg</p>
</blockquote>
<h2 id="해결">해결</h2>
<p>저는 이 문제를 <code>rel=&quot;preload&quot;</code>를 link 태그 안에 넣어주어 
웹폰트가 HTML 파싱중에 로드되도록 하여 해결했습니다🙂</p>
<pre><code>// 변경 전
&lt;link href=&quot;/font.otf&quot; as=&quot;font&quot; type=&quot;font/otf&quot; crossorigin /&gt;

// 변경 후
&lt;link rel=&quot;preload&quot; href=&quot;/font.otf&quot; as=&quot;font&quot; type=&quot;font/otf&quot; crossorigin /&gt;</code></pre><p>이 외에도 최적화를 위해
다른 폰트 확장자를 사용하거나,
@font-face의 font-display 옵션으로 웹폰트 로딩동안 콘텐츠를 어떻게 보여줄 것인지(FOIT, FOUT)
여러 방법을 사용해 볼 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] VAC Pattern]]></title>
            <link>https://velog.io/@jin_h2_/React-VAC-Pattern</link>
            <guid>https://velog.io/@jin_h2_/React-VAC-Pattern</guid>
            <pubDate>Sat, 20 Jul 2024 09:24:28 GMT</pubDate>
            <description><![CDATA[<p>실무에서 VAC 패턴을 적용하게 되어 알아본 디자인 패턴..!</p>
<h2 id="vac-패턴">VAC 패턴</h2>
<p>VAC는 View Asset Component의 약자로 View 컴포넌트에서 JSX 영역을 Props Object로 추상화하고, JSX를 VAC로 분리해서 개발하는 설계 방법을 말합니다.
비즈니스 로직과 View 로직(JSX, Style 영역)이 분리되므로 FE, UI 개발 영역이 겹침으로 인한 코드 충돌을 방지할 수 있는 장점이 있습니다.</p>
<h3 id="vac의-특징">VAC의 특징</h3>
<ol>
<li>반복, 조건부 노출, 스타일 제어와 같은 렌더링과 관련된 처리만을 수행</li>
<li>props를 통해서만 제어되며, 스스로의 상태를 관리하거나 변경하지 않는 stateless 컴포넌트</li>
<li>이벤트에 함수를 바인딩할 때 추가 처리 없이 적용 (항상 이벤트 객체가 전달됨)</li>
</ol>
<p><img src="https://velog.velcdn.com/images/jin_h2_/post/e68c54d4-546e-4856-822a-0010d1399ff7/image.png" alt=""></p>
<h3 id="구현-예시">구현 예시</h3>
<pre><code>// VACView Component
const SpinBoxView = ({ value, onIncrease, onDecrease }) =&gt; (
  &lt;div&gt;
    &lt;button onClick={onDecrease}&gt;-&lt;/button&gt;
    &lt;span&gt;{value}&lt;/span&gt;
    &lt;button onClick={onIncrease}&gt;+&lt;/button&gt;
  &lt;/div&gt;
);</code></pre><pre><code>// VACV
const SpinBox = () =&gt; {
  const [value, setValue] = useState(0);

  const props = {
    value,
    onDecrease: () =&gt; setValue(value - 1),
    onIncrease: () =&gt; setValue(value + 1),
  };

  // JSX를 VAC로 교체
  return &lt;SpinBoxView {...props} /&gt;;
};</code></pre><p>조심해야 할 것은, View 컴포넌트의 기능이나 상태 제어에 VAC가 관여해서는 안됩니다.</p>
<h3 id="참고">참고</h3>
<p><a href="https://wit.nts-corp.com/2021/08/11/6461">https://wit.nts-corp.com/2021/08/11/6461</a>
<a href="https://tv.naver.com/v/23162062">https://tv.naver.com/v/23162062</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[vercel에 환경변수를 등록하자]]></title>
            <link>https://velog.io/@jin_h2_/vercel%EC%97%90-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98%EB%A5%BC-%EB%93%B1%EB%A1%9D%ED%95%98%EC%9E%90</link>
            <guid>https://velog.io/@jin_h2_/vercel%EC%97%90-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98%EB%A5%BC-%EB%93%B1%EB%A1%9D%ED%95%98%EC%9E%90</guid>
            <pubDate>Tue, 09 Jan 2024 09:36:40 GMT</pubDate>
            <description><![CDATA[<p>vercel에 배포를 하고 나서 무언가 이상함을 느꼈습니다.
다행히 중요한 키가 없었지만 <code>.env</code>를 github에 올리는 실수를 했고, <code>.gitignore</code>에 추가한 후 환경변수를 추가하여 다시 github에 push, vercel에 배포를 했는데 로컬에서 되던 게 안되더라구요.
환경변수를 인식하지 못하기 때문이란 건 바로 알아챌 수 있었습니다.</p>
<h2 id="vercel에-환경변수-등록하기">vercel에 환경변수 등록하기</h2>
<blockquote>
<p><strong>Settings &gt; Environment Variables</strong> 에서 등록해 주세요.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/jin_h2_/post/fa95fc7f-5a3f-4d65-b693-f92520825fc1/image.png" alt="">
등록한 후 재배포를 진행해주면 끝🙂</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ChunkLoadError: Loading chunk]]></title>
            <link>https://velog.io/@jin_h2_/ChunkLoadError-Loading-chunk</link>
            <guid>https://velog.io/@jin_h2_/ChunkLoadError-Loading-chunk</guid>
            <pubDate>Mon, 08 Jan 2024 11:36:55 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>ChunkLoadError: Loading chunk</p>
</blockquote>
<p>프로젝트를 진행하면서 로컬에서 테스트 중 발생한 청크 에러. 
<img src="https://velog.velcdn.com/images/jin_h2_/post/9156b0f6-7f98-4609-b185-ae7482486c49/image.png" alt="ChunkLoadError"></p>
<h1 id="원인">원인</h1>
<p>웹팩 설정을 하지 않으면 js파일이 청크파일로 나뉘면서 해시값을 부여받게 됩니다.
청크파일이 바뀌게 되면 해시값도 바뀌게 되는데,
이 때 서버에는 파일명이 변경된 새로운 파일이 배포되어 있고, 사용자의 브라우저에 변경 전 파일이 캐시되어 있을 경우 이를 서버에 요청을 하기 때문에 발생하는 것이라고..</p>
<h1 id="해결">해결</h1>
<p>다양한 해결 방법이 있는데 로컬에서 발생했으니 캐시를 지우는 것부터 시작해보았습니다.
캐시를 지우고도 에러가 발생하여 build를 다시 하여 파일이 다시 변경되도록 해보자 생각하고 진행해보았습니다.
build를 진행하고 한 번 더 캐시를 지워준 후 npm run dev를 해주니 해결!</p>
<p>하지만 찝찝합니다.. 참고한 블로그들을 보았을 땐 다양한 원인과 해결 방법이 기재되어 있었는데 그거에 비해 간단하게 해결이 된 거 같아요.
원인을 잘 이해하고 해결한 것이 맞는건지..🤔</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[토이프로젝트 MY NOTE]]></title>
            <link>https://velog.io/@jin_h2_/%ED%86%A0%EC%9D%B4%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-MY-NOTE</link>
            <guid>https://velog.io/@jin_h2_/%ED%86%A0%EC%9D%B4%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-MY-NOTE</guid>
            <pubDate>Tue, 14 Nov 2023 13:02:15 GMT</pubDate>
            <description><![CDATA[<h1 id="my-note">My Note</h1>
<h2 id="프로젝트를-하게-된-계기">프로젝트를 하게 된 계기</h2>
<p>팀 프로젝트만 하다보니 혼자서 <code>CRUD</code>를 해본 적이 없어서 기본기를 다져보고자 짬을 내 프로젝트를 완성했습니다.
더불어 Sass, CSS module을 사용해보았지만 가장 많이 사용하고 있는 CSS in JS <code>styled components</code>를 사용해 본 적이 없었기 때문에 이를 학습하는 것 또한 목적이었고요.</p>
<p>창의적인 프로젝트를 제작하면 좋았겠지만 사이드 프로젝트와 취업준비를 병행중이라 흔히 만들어보는 투두리스트와 비슷하게 노트로 만들었습니다. 1인용 노트앱이기 때문에 리액트로..:)</p>
<h2 id="개발과정">개발과정</h2>
<p><a href="https://jininii.github.io/my-note-project/">My Note</a>에서 확인하실 수 있습니다!</p>
<h3 id="1-메인--노트-생성">1. 메인 / 노트 생성</h3>
<ul>
<li>별도의 서버가 필요하지 않고, 데이터양이 많지 않으므로 <code>Local Storage</code>에 데이터를 저장하는 것으로 했습니다.
데이터 저장 시에는 JSON 자료형으로만 저장이 가능하기 때문에 <code>JSON.stringify()</code>를 사용해 저장하고, 데이터를 불러올 때에는 원래의 자료형으로 불러와 줍니다. <code>JSON.parse</code></li>
<li>생성이 완료된 노트에도 id 값이 필요합니다.
<code>uuid</code>라는 모듈을 사용해 고유한 key 값을 생성할 수 있었습니다.</li>
</ul>
<h3 id="2-수정-및-삭제">2. 수정 및 삭제</h3>
<ul>
<li>노트 전체 내용을 보는 페이지에서 수정과 삭제가 가능합니다.</li>
<li>노트를 누르면 <code>useParams</code>로 id 값을 확인하고, 데이터를 불러옵니다.</li>
<li><code>spread 연산자</code>와 <code>map</code> 함수를 사용해 배열을 수정하는 방법으로 노트를 수정합니다.</li>
<li>삭제는 <code>filter</code> 함수를 사용해 삭제하고자 하는 노트의 id와 일치한 데이터를 지워주고, 일치하지 않은 데이터는 남겨놓게 하였습니다.</li>
</ul>
<h3 id="3-검색">3. 검색</h3>
<p>(작성중..)</p>
<h2 id="🗨️-회고">🗨️ 회고</h2>
<h3 id="styled-components를-학습하며">styled-components를 학습하며.</h3>
<p>SCSS와 같은 전처리기만 사용했던지라 styled-components에 익숙해지기가 꽤 오래 걸렸습니다😢
사용해보니 css 파일을 만들지 않아도 되니 폴더 내 파일 수가 현저히 줄어들었고, css 우선 순위를 생각하지 않아도 되는 부분이 편리했습니다. 사용하지 않는 코드도 쉽게 알 수 있었고요.
자바스크립트 기능을 스타일링에 적용할 수 있다고 하는데 아직 이 부분을 크게 활용해보지 못했습니다.</p>
<p>styled-components는 클라이언트 런타임에 스타일을 생성하기 때문에 깜박임이 있다던가 하는 성능 이슈가 발생할 수 있다는 점이 단점으로 꼽히기 때문에 CSS in CSS와 CSS in JS 어떤 것을 선택할 지 프로젝트 규모를 생각해서 결정해야 할 거 같다고 생각합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Received `true` for a non-boolean attribute. 에러]]></title>
            <link>https://velog.io/@jin_h2_/Received-true-for-a-non-boolean-attribute</link>
            <guid>https://velog.io/@jin_h2_/Received-true-for-a-non-boolean-attribute</guid>
            <pubDate>Tue, 14 Nov 2023 09:43:47 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Warning: Received <code>true</code> for a non-boolean attribute <code>primary</code>.
If you want to write it to the DOM, pass a string instead: primary=&quot;true&quot; or primary={value.toString()}.</p>
</blockquote>
<p>개인 토이프로젝트를 진행하면서 <code>styled-components</code>를 학습중이었는데
아래 코드에 대해 에러가 발생하였습니다.</p>
<pre><code class="language-javascript">const Button = styled.button`
  font-size: 16px;
  height: 40px;
  padding: 0 20px;
  border-radius: 5px;

  color: ${props =&gt; props.color};
  background: ${props =&gt; props.background};

  ${props =&gt;
    props.primary &amp;&amp;
    css`
      color: white;
      background-color: #d74521;
    `}
`;

&lt;Button $primary&gt;저장&lt;/Button&gt;</code></pre>
<h2 id="원인-및-해결">원인 및 해결</h2>
<p>에러의 원인은 HTML DOM 요소에 비표준 속성이 첨부되고 있기 때문입니다.</p>
<p><a href="https://developer.mozilla.org/ko/docs/Web/HTML/Attributes">HTML 표준 속성</a>이 아닌 속성을 사용하면 에러가 발생한다는 것.
5.1 버전부터는 props 앞에 <code>$</code>를 붙여 props가 실제 DOM 요소로 렌더링 되는 것을 방지해 줍니다.</p>
<pre><code class="language-javascript">// transient props
const Comp = styled.div`
  color: ${props =&gt;
    props.$draggable || &#39;black&#39;};
`;

render(
  &lt;Comp $draggable=&quot;red&quot; draggable=&quot;true&quot;&gt;
    Drag me!
  &lt;/Comp&gt;
);</code></pre>
<p>자세한 내용과 다른 해결 방법은 <a href="https://styled-components.com/docs/api#transient-props">공식문서</a>에서 확인할 수 있습니다.</p>
<p>저는 아래처럼 수정해서 해결했습니다:)</p>
<pre><code class="language-javascript">// 수정 후
${props =&gt;
    props.$primary &amp;&amp;
    css`
      color: white;
      background-color: #d74521;
    `}

&lt;Button $primary&gt;저장&lt;/Button&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Typescript] Interface와 Type]]></title>
            <link>https://velog.io/@jin_h2_/Typescript-Interface%EC%99%80-Type</link>
            <guid>https://velog.io/@jin_h2_/Typescript-Interface%EC%99%80-Type</guid>
            <pubDate>Wed, 01 Nov 2023 07:17:13 GMT</pubDate>
            <description><![CDATA[<h1 id="공통점">공통점</h1>
<p>Typescript에서 타입을 정의할 때 사용하는 2가지 방법 <code>interface</code>와 <code>type</code>.
비슷한 거 같은데 왜 나눠놓았는지 궁금해서 찾아봤습니다🙂</p>
<p>공통점을 먼저 보자면,
두 방법 모두 객체의 타입이나 함수의 타입을 정의할 수 있고
변수 타입, 함수의 반환값을 지정해주며, 확장이 가능하다는 공통점이 있습니다.</p>
<pre><code class="language-javascript">type User = {
  name: string,
  age: number
}
interface User = {
  name: string,
  age: number
}</code></pre>
<h1 id="차이점">차이점</h1>
<h2 id="type-alias">Type Alias</h2>
<p>타입 별칭이라고 부르며, 새로운 타입을 정의하고 싶을 때 사용합니다.</p>
<h3 id="원시값-타입-선언">원시값 타입 선언</h3>
<pre><code class="language-javascript">type str = string;</code></pre>
<h3 id="intersection-type---확장">Intersection Type(&amp;) - 확장</h3>
<p>interface동일한 이름으로 선언을 하면 오류가 나고, <code>&amp;</code>을 사용하여 확장만 가능합니다.</p>
<pre><code class="language-javascript">type User = {
  name: string,
  age: number
}
type Student = User &amp; {
  country: string
}

const kim: Student = {
  name: &#39;Kim&#39;,
  age: 20,
  country: &#39;South Korea&#39;
}</code></pre>
<h3 id="유니온-사용">유니온(|) 사용</h3>
<pre><code class="language-javascript">type fruit = &#39;banana&#39; | &#39;apple&#39; | &#39;grape&#39;</code></pre>
<h3 id="튜플">튜플</h3>
<pre><code class="language-javascript">type Tuple = [string, number];</code></pre>
<h2 id="interface">Interface</h2>
<h3 id="선언-병합">선언 병합</h3>
<p>interface는 type과 달리 동일한 이름을 사용하면 병합이 되며 확장됩니다.</p>
<pre><code class="language-javascript">interface Box {
  weight: number;
}
interface Box {
  height: number;
}

const container: Box = {
  weight: 100,
  height: 100
}</code></pre>
<h3 id="extends">extends</h3>
<p>extends를 사용해 확장도 가능합니다.</p>
<pre><code class="language-javascript">interface User {
  name: string;
  age: number;
}
interface Student extends User {
  country: string;
}

const kim: Student = {
  name: &#39;Kim&#39;,
  age: 20,
  country: &#39;South Korea&#39;
}</code></pre>
<h2 id="무엇을-써야하는지-고민이라면">무엇을 써야하는지 고민이라면</h2>
<p>튜플이나 원시값 타입 선언 등 <code>Type</code>을 써야할 때가 아니라면 가능한 <code>Interface</code>를 사용합니다.
이게 Typescript 팀에서 권장하는 방향이라고 하네요🙂</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🏢기업협업 회고]]></title>
            <link>https://velog.io/@jin_h2_/%EA%B8%B0%EC%97%85%ED%98%91%EC%97%85-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@jin_h2_/%EA%B8%B0%EC%97%85%ED%98%91%EC%97%85-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Thu, 26 Oct 2023 07:29:22 GMT</pubDate>
            <description><![CDATA[<h1 id="🏢-기업협업-시작">🏢 기업협업 시작</h1>
<p>9월 25일 ~ 10월 27일, 그 사이 긴 추석연휴가 있어 약 4주 정도 되는 기간동안 인턴십을 가졌습니다.</p>
<h2 id="🗨️-내가-선택한-곳과-이유">🗨️ 내가 선택한 곳과 이유</h2>
<p>인턴십을 가기 전에 나는 어떤 회사에서 일하고 싶은지를 고민해보았습니다.
기업이 어떤 것을 추구하느냐가 제게 큰 동기부여가 될 것 같아 가장 중요하게 느껴졌습니다.
리스트 중 희망하는 기업을 작성할 수 있었는데, 저는 그 중 1순위로 작성한 기업에 다녀올 수 있었습니다🙂</p>
<p>💙열다컴퍼니
옷장을 정리하고, 패션 라이프사이클에 맞춰 의류 소비 및 처분을 도와주는 서비스를 운영하는 기업으로,  1인 또는 맞벌이 가구가 늘어나고 있는 현황과, 의류폐기물과 패스트패션에 대한 문제에 공감하고 있던 저에겐 이 회사가 가장 눈에 띄었어요.</p>
<p>기술적으로는 현재 가장 많이 사용되고 있는 next.js와 typescript를 사용하고 있고, 본격적으로 개발을 배우기 전에 혼자 flutter를 찍먹해보기도 했었던지라 회사소개란에 앱 개발 예정이라는 것도 흥미로웠고요.</p>
<hr>
<h2 id="프로젝트-소개">프로젝트 소개</h2>
<p>아쉽게도 같이 가게 된 백엔드 개발자 양지은님과는 다른 업무를 맡게 되었습니다.
회사의 매출이 점차 커지면서 사용중인 노코드 툴을 인하우스로 마이그레이션 하고자 하는 페이지가 있었고, 그 페이지를 구현하는 업무를 맡았습니다.</p>
<h3 id="🔧기술-스택">🔧기술 스택</h3>
<p><code>next.js</code>, <code>typescript</code></p>
<hr>
<h2 id="💻-맡은-업무">💻 맡은 업무</h2>
<h3 id="1-어드민-페이지-마이그레이션-진행-중단">1. 어드민 페이지 마이그레이션 (진행 중단)</h3>
<blockquote>
<p>사내 임직원 및 매니저, 고객의 정보가 저장되는 어드민 페이지로, 예약정보와 진행상태, 그리고 매출을 확인할 수 있습니다.</p>
</blockquote>
<h4 id="1-1-ui-기획">1-1. UI 기획</h4>
<p>당시 디자이너가 없는 관계로 UI 기획을 진행하였습니다.
<img src="https://velog.velcdn.com/images/jin_h2_/post/b3c1945a-0e31-4071-8276-5d310ceef5e6/image.png" alt="어드민 메인화면"> <img src="https://velog.velcdn.com/images/jin_h2_/post/79669ddd-ff8e-4255-8c34-ab3a29e12719/image.png" alt="어드민 대시보드"></p>
<hr>
<h3 id="2-🌟배포완료🌟-서비스-예약-페이지">2. 🌟배포완료🌟 서비스 예약 페이지</h3>
<blockquote>
<p>서비스 예약 페이지는 고객이 직접 구매 예정인 서비스에 필요한 인원 수와 이용 시간을 선택할 수 있고, 선택 및 작성한 내용에 따라 매니저를 배정하여 서비스를 받아볼 수 있게 되어있습니다.
회사가 소비자에게 제공하는 주요 서비스로, 매출과 직결되는 페이지를 마이그레이션 하는 작업이었기 때문에 부담도 되고 열심히 해야겠다는 다짐과 함께 UI 계획부터 시작했습니다.</p>
</blockquote>
<p>해당 페이지는 배포가 완료되어 아래 주소에서 확인이 가능합니다!
<a href="https://www.yolda.me/form/reservation">페이지 바로 보기</a>
또는 메인페이지에서 로그인 후 바로 확인할 수 있습니다.
[메인페이지 바로 가기] (<a href="https://www.yolda.me/">https://www.yolda.me/</a>)</p>
<h4 id="2-1-ui">2-1. UI</h4>
<p><img src="https://velog.velcdn.com/images/jin_h2_/post/b97e8019-07a7-421c-a62e-55e068eaf7bb/image.png" alt=""></p>
<p>웹앱으로 페이지 제작을 진행하였습니다.
서비스에 가장 중요한 선택옵션과 이해를 위한 설명 제공 기준으로 페이지를 나눴습니다. 총 4페이지로 구성되어 있고, 직관적인 이동 버튼을 하단에 두었습니다.</p>
<h4 id="2-2-기능구현">2-2. 기능구현</h4>
<blockquote>
</blockquote>
<ul>
<li><strong>선택한 매니저 인원수에 따라 선택 가능한 시간 버튼 호출</strong></li>
<li><strong>선택한 매니저 인원수와 시간에 대한 서비스 안내 표기</strong></li>
<li><strong>props로 유저가 선택 및 작성한 데이터를 컴포넌트에 전달하여 최종 제출되도록 구현</strong>
처음에는 Context API를 쓰는 것을 생각해 코드를 짰는데, useSatet와 props로 데이터를 전달하는 방식으로 변경하였습니다.
Context API를 사용하고 싶었으나 Typescript의 에러를 해결하면서 코드를 짜다보니 시간이 지체되기도 했으며 배운대로 적용에 실패하였고😭..
결국 CEO님께 이 상황을 말씀드리고 props로 전달하는 것으로 코드를 구성하는 것은 어떤지 여쭤보았습니다. 규모가 크지 않으니 props로 넘기는 방법도 간단하고 좋아보인다고 말씀해 주셔서 완성했습니다!</br></li>
<li><strong>Next Auth, useSession Hook으로 유저 확인 및 정보 자동기입</strong>
소셜로그인을 관리하는 라이브러리로 JWT 방식의 인증을 사용하는 Next Auth를 활용. 유저의 Id 여부를 판단하여 없을 경우 로그인 페이지로 이동하고, 정보가 있다면 이름, 전화번호를 input과 state에 자동으로 업데이트 되도록 하였습니다.</br></li>
<li><strong>enum 사용하여 상수데이터 가져오기</strong></li>
<li><strong>useRef를 활용해 경우에 따라 input창으로 자동 포커싱</strong>
프로모션 코드를 입력해야하는 옵션을 선택했을 경우 자동으로 코드 입력 input에 포커싱 되도록 구현</li>
<li><strong>다음 주소검색 API를 사용하여 주소 입력창 구현</strong>
고객으로부터 입력에 대한 불편 컴플레인이 접수되어 주소 검색 버튼 클릭 시 팝업으로 띄우던 것을 모달로 변경</li>
<li><strong>useEffect를 사용해 쿼리 파라미터 여부 확인하고, 파라미터를 가져와 프로모션 코드 자동 입력, 알게된 경로 옵션 자동 선택되도록 구현</strong></li>
</ul>
<h4 id="2-3-문제-해결">2-3. 문제 해결</h4>
<blockquote>
<ul>
<li><strong>문항에 따른 옵션 버튼을 누르면 쿼리스트링이 생기면서 제출되는 현상</strong>
해당 문제에 대해 포스트를 작성했습니다.
<a href="https://velog.io/@jin_h2_/button%EC%9D%84-%ED%81%B4%EB%A6%AD%ED%96%88%EB%8A%94%EB%8D%B0-%EC%BF%BC%EB%A6%AC%EC%8A%A4%ED%8A%B8%EB%A7%81%EC%9D%B4-%EC%83%9D%EA%B8%B0%EB%8A%94-%EB%AC%B8%EC%A0%9C">button 문제 해결</a><br></li>
</ul>
</blockquote>
<ul>
<li><strong>상수데이터의 value를 호출했을 때 개행이 되지 않는 현상</strong>
해당 문제에 대해 포스트를 작성했습니다.
<a href="https://velog.io/@jin_h2_/React-rn%EF%BC%BCr%EF%BC%BCn-%EC%A4%84%EB%B0%94%EA%BF%88%EA%B0%9C%ED%96%89%EC%9D%B4-%EC%95%88%EB%90%A0-%EB%95%8C-CSS-white-space-%EC%86%8D">개행 문제 해결</a><br></li>
<li><strong>Module not found: Can&#39;t resolve &#39;child_process&#39;</strong>
시간을 ISO형식으로 변환하여 보내고자 했는데, 만들어둔 메서드 함수가 있어서 이를 사용해달라는 요청이 들어와 적용했더니 나타난 에러.
nods.js의 내장모듈로 클라이언트 측에서 사용이 불가능해서 나타난 거라고 하는데 즉 서버에서 사용중인 컴포넌트라서 안된다는 거! 이런 일이 생길거라곤 생각도 못했다...
최대한 제 선에서 해결해보고자 하여 api 엔드포인트를 생성해보라는 말이 있길래 시도해봤으나 404 에러가 발생했습니다.
결국 CTO님께 말씀드려서 프론트단에서 변환하여 보내는 것으로 해결했습니다.</li>
<li><strong>TypeError: Cannot read properties of undefined</strong>
값이 정의되지 않아 읽을 수 없을 때 나타난다고 합니다.
여러 방법이 있지만 저의 경우 옵셔널 체이닝을 써주어 해결했습니다.</li>
</ul>
<h4 id="2-4-피드백-반영">2-4. 피드백 반영</h4>
<blockquote>
</blockquote>
<ul>
<li><strong>useEffect를 쓰지 않고 스크롤바 위치 초기화</strong>
컴포넌트가 전환될 때 스크롤바가 이전 컴포넌트에 있던 위치에 그대로 머물러있어 스크롤을 올려야 하는 불편함이 있었습니다.
때문에 useEffect에 코드를 넣어주었는데 잦은 useEffect 사용을 지양하고자 useEffect 없이도 가능한 지 검토해보라는 피드백을 받았습니다.<br>
이동 버튼을 눌렀을 때 다음 또는 이전 컴포넌트로 변경되도록 만든 함수가 있었는데, 해당 함수로 코드를 옮겼습니다.
<br>찾아보니 공식문서에 <code>ScrollRestoration</code>를 이용한 스크롤 위치 초기화 방법이 있는데 라우트 했을 때 작동되는 것 같아 기회가 된다면 사용해 봐야겠습니다.
<a href="https://reactrouter.com/en/6.11.1/components/scroll-restoration">ScrollRestoration - 공식 문서</a></li>
</ul>
<h4 id="2-5-기억나거나-고민-많았던-코드">2-5. 기억나거나 고민 많았던 코드</h4>
<blockquote>
<ul>
<li><strong>useEffect를 사용하지 않고 스크롤을 초기화하는 코드</strong>
조금 더 깊게 고민했었다면 useEffect를 안 썼어도 됐었겠구나 했습니다..</li>
</ul>
</blockquote>
<pre><code>const next = () =&gt; {
        if (current &lt; 5) {
            setCurrent((prev) =&gt; prev + 1);
            window.scrollTo(0, 0);
        }
    };</code></pre><ul>
<li><strong>date 유형의 input 태그</strong>
배포 후 아이폰으로 보니 date 유형인 input창이 사이즈도 이상하고, 선택 날짜를 제한하고자 넣은 min 속성이 안돼서 많은 수정을 거쳤습니다.
알고보니 iOS에서는 min 속성을 지원하지 않는다고...
그래서 최소 선택 가능 날짜 이전으로 선택하면 선택이 되지 않고, 경고문을 띄워주는 함수를 만들었습니다.
input의 width도 도저히 해결이 안되어서 고민하던 찰나, date 유형의 input에 <code>opacity: 0</code>을 주어서 해결할 수 있었습니다.
가짜 input에 선택한 값을 보여주도록 설정해 주고요.
웹브라우저마다 지원이 다르다는 것은 익숙한데, 디바이스까지는 미처 생각을 못했는데 모바일 디바이스도 꼭꼭 테스트를 해야겠습니다..!😢<br></li>
<li><strong>프로모션 코드를 기입하는 input 위치 변경</strong>
유저의 경험을 생각하여 기존 폼과 다르게 코드 입력 input의 노출 조건과 위치를 변경해 보았습니다.
프로모션 코드는 알게 된 경로를 선택하는 옵션에 따라 필수 기재 사항인데, 기존 폼대로 만들었을 땐 기입하는 칸이 옵션보다 위에 있어서 옵션을 선택하면 useRef로 코드 입력칸에 포커싱 되도록 했고, 입력이 필요없는 옵션을 선택했다면 입력이 되지 않도록 로직을 구현했습니다.
그렇게 만들고나니 코드를 입력할 필요가 없는 유저라면 혼란스러울 것 같기도 하고, 코드를 입력할 유저도 다시 상단으로 옮겨야하니 불편하겠단 생각이 들었습니다.
위치만 바꿔준다면 좀 더 편리해질 거 같단 생각이 들었고, 코드 입력창이 옵션을 눌렀을 때 뜨면 혼동이 없을 거 같아 CEO님께 말씀드려서 위치를 변경했습니다. 여기에 추가로 코드 입력이 필요한 옵션을 선택했을 때에만 input이 뜨도록 해 구현을 마쳤습니다🙂</li>
</ul>
<h3 id="3-매니저---서비스-결과-제출-페이지-85-완료">3. 매니저 - 서비스 결과 제출 페이지 (85% 완료)</h3>
<blockquote>
<p>서비스를 제공 완료한 매니저가 결과에 대한 정보를 기입하여 제출하는 페이지입니다.</p>
</blockquote>
<p>해당 페이지는 제출된 데이터를 받을 API가 완성되지 않아 제출 기능은 되지 않지만 이를 제외하고 배포가 된 상태입니다!
로컬에서 테스트 했을 땐 크게 문제가 없었으니..😂</p>
<h4 id="3-1-ui">3-1. UI</h4>
<p>서비스 결과 보고 페이지이므로 공개가 어렵습니다🙂</p>
<h4 id="3-2-기능구현">3-2. 기능구현</h4>
<blockquote>
<ul>
<li><strong>쿼리 파라미터로 예약정보를 불러오는 id값을 확인하여, 해당 예약 정보를 제공하도록 구현</strong>
<code>router.isReady</code>를 사용해 쿼리 파라미터에 접근</li>
</ul>
</blockquote>
<ul>
<li><strong>서비스 전과 후의 사진을 S3에 업로드, 각각의 디렉토리에 저장되도록 구현</strong></li>
</ul>
<h4 id="3-3-문제-해결">3-3. 문제 해결</h4>
<blockquote>
<ul>
<li><strong>Cors Error</strong>
<code>Cross-Origin Resource Sharing</code>
브라우저에서 동일 출처가 아니면 에러가 발생하는데, 서버에서 헤더에 허용할 출처를 기재해 클라이언트에 응답해주면 된다고 합니다.
크롬 확장 프로그램을 설치해서 해결하는 방법도 있는데, 여러모로 복잡해지는 것 같아 API로부터 받아오는 JSON 데이터를 mockdata로 만들어 테스트해보고, 배포하여 문제 발생 시 해결하는 것으로 진행했습니다.
종종 발생하는 에러인 거 같아 기억해두어야겠습니다!<br></li>
<li><em>413 Request Entity Too Large*</em>
서버에서 지정한 용량보다 큰 용량의 이미지를 업로드 해서 그런 것으로 알고 CTO님께 말씀드렸는데 알고보니 업로드 할 버킷 패스를 잘못 입력을 했다는 것을 알게 되었습니다. 알맞은 디렉토리를 입력해주니 해결된..😂</li>
</ul>
</blockquote>
<h3 id="🤩-배포가-되었다-">🤩 배포가 되었다 !</h3>
<p>여태 프로젝트들을 전부 로컬에서만 봤던지라 실제로 서비스에 사용되는 것은 처음이어서 설레기도 했고, 메인에서 바로 이어지고 매출과 직결되는 페이지이기 때문에 긴장도 컸습니다.
그래서 바로 터져버린 date 형식의 input 문제로 멘붕이 왔었네요😢</p>
<p>고객의 예약신청이 완료되면 메시지로 받아볼 수 있는데 하나 둘씩 올라오는 예약완료 메시지가 뜰 때마다 신기하고, 이 맛에 개발한다는 생각이 들었어요. 개발자가 되고 싶었던 이유 중 하나였거든요.</p>
<p>유저플로우를 나름 신경써서 만들었다고 생각했으므로, input 문제까지 해결하고 나서부터는 할 일을 다 마쳤다고 생각했는데 그건 크나큰 오산이었다는 거..</p>
<h4 id="문제발생-및-해결">문제발생 및 해결</h4>
<p><strong>1. 🙎‍♂️🙎‍♀️ : 팝업창이 불편해요.</strong>
주소를 검색할 때 팝업창이 뜨도록 했는데 모바일에서는 팝업을 허용하지 않으면 설정에 들어가서 허용해줘야하는 번거로움이 있어 이 부분이 불편하다는 고객의 의견이 있었습니다.
인턴십 막바지였어서 그런지 CTO님이 팝업이 아닌 다른 방식으로 가능한 지 여부만 알아보라고 하셨는데, 모달창으로 충분히 구현이 가능한 부분이라고 생각을 했고, 최대한 제 손으로 끝내고 싶어서 구현을 마친 후 모달창이 뜨도록 개선된 화면을 보여드렸고 좋은 반응을 얻을 수 있었습니다.
<img src="https://velog.velcdn.com/images/jin_h2_/post/44d1cc70-4fe8-473b-a3eb-8fc994ed986b/image.png" alt="팝업에서 모달로"></p>
<p>** 2. 예약신청 제출 중복 클릭 문제 **
제출 버튼을 누르고 정상적으로 데이터가 서버로 전달되면 다음 페이지로 넘어가게끔 했기 때문에 테스트 할 땐 당연히 다음 페이지로 넘어가기를 기다렸었는데 유저 입장에선 그게 아니라는 것을 미처 생각하지 못했었습니다..😨
유효성 검사를 거쳐 버튼 활성화 여부를 바꿔주고 있었는데, 위와 같은 문제가 발생하여 제출 버튼을 클릭하면 활성화 되어있던 버튼의 disabled 속성을 비활성화 해주는 코드를 추가해 주었습니다.  </p>
<h2 id="느낀-점">느낀 점</h2>
<p><strong>1. 질문을 두려워 하지 말자.</strong>
CEO님께 프론트엔드 업무를 여쭤보고 있었는데, 잦은 미팅으로 뵙기가 어려웠습니다. 때문에 CTO님과 같이 있는 시간이 길었는데 CTO님은 프론트엔드를 잘 모른다고 하셔서 코드를 치다가 어려움이 있을 때 누구에게 질문해야할 지 막막했었습니다.
두 번째로 진행한 서비스 결과 제출 페이지를 만들 때 S3에 이미지 업로드를 하는 부분에서 막혔었는데 혼자 하루동안 고민하며 해결하려고 했었습니다.
CTO님께서 진행하다가 어려운 점은 없었냐고 하셔서 그제서야 말씀을 드렸고, 너무나도 간단하게 답을 주셔서 막혔던 진행이 술술 풀리기 시작했습니다.
S3를 이전부터 사용하고 있었던지라 당연히 CTO님께 여쭤봤으면 금방 해결했을텐데 이 부분을 간과하고 혼자 너무 오랜 시간을 고민했던 거 같습니다.
후에는 혼자 해볼 수 있는데까지 해보고, 그럼에도 해결이 어려울 때에 <strong>어떠한 문제에서 어디까지 해봤는데도 해결되지 않았는지</strong> 말씀을 드리며 질문을 했습니다.
그렇게하니 혼자 하루 꼬박 고민하는 것보다 빠르고 명쾌하게 해결할 수 있었고, 서로 잘 모르는 문제라면 함께 고민하며 해결해주려고 하셔서 감사했습니다!
나중에 알게 된 것이, 서버에서 해결이 가능하거나 쉽게 확인이 가능한 부분도 혼자 끙끙거리며 고민하고 있었더라고요. 이 때 팀원과의 소통의 중요성도 깨닫게 되었습니다🙂</p>
<p><strong>2. 유저의 행동이 나와 같다고 생각하지 말자.</strong>
저는 스스로 UI/UX를 신경써서 계획하고 코드를 짠다고 생각했었는데 이번 배포 경험으로 개발자인 본인 위주로 생각을 많이 했다는 것을 깨달았습니다. 사소한 버그가 유저에게 불편함을 느끼게 할 수 있다는 것은 물론이거니와, 불편했을 팀원들에게도 죄송한 마음이 들었습니다😭
설마 유저가 이렇게까지 행동하겠어? 라는 생각이 든다면 그냥 넘기지 말고 처리를 해야겠습니다.</p>
<p><strong>3. 직접 해봐야 는다.</strong>
next.js와 typescript는 협업에서 처음 사용해 보았는데, 협업에 나서기 전에 강의를 들으며 공부를 하긴 했지만 역시나 직접 해보는 것만큼 좋은 것이 없다는 걸 느꼈습니다.
이미 다른 프론트엔드 개발자분이 짜둔 코드가 있어 그 코드를 보며 왜 이렇게 짠 것인지 고민해보고, 직접 적용해보기도 하고, 에러메시지들을 해결하며 강의에서는 보지 못한 지식을 습득할 수 있었습니다🙂</p>
<h2 id="아쉬웠던-것은">아쉬웠던 것은</h2>
<p><strong>1. 질문을 망설였다</strong>
처음부터 계획과정에 질문을 하지 않아 계속 추가되는 로직에 추가와 수정하는 시간이 길었고, 모르는 것을 혼자 해결하겠다고 많은 시간을 허비한 것입니다.
처음에는 바쁜 팀원들을 위한 배려라고 생각했는데 CTO님과 대화를 하면서부터 작업속도가 붙는 것을 보고 잘못된 판단으로 쪼개어 알차게 써야하는 시간을 허투루 소비한 것 같아 아쉬움이 남습니다.</p>
<p><strong>2. 남들에게 이해가 되는 코드를 쳤는가</strong>
유저나 팀원들에게 좋은 코드를 짜는 개발자가 되고 싶은데 나는 그런 사람이었는가? 스스로 묻는다면 아니었다고 답할 수 있는 시간이었습니다..
점점 늘어나고 쪼개지는 변수로 변수명을 어떻게 작성해주어야 할 지 모르겠고, 그 과정에 나만 알 수 있는, 명확하지 않는 변수명으로 코드를 쳐온 거 같습니다.
리팩토링하여 가독성을 높이고 싶었는데 시간적 여유가 없었다고 하면 변명이겠죠?😢 스스로 찝찝한 코드로 협업을 마무리하게 되어 아쉬움이 납습니다.</p>
<h2 id="👍결과">👍결과</h2>
<p>서비스 예약 페이지를 마이그레이션 한 이후 예약건수가 전환 전 대비 1.5배 상승하였습니다. First Paint가 2.6초에서 1.0초로, Speed Index는 3.5초에서 1.3초로 단축되었음을 확인했습니다!
조금 더 성능을 높이는 작업을 할 수 있었을텐데 그러지 못해 아쉽습니다.</p>
<h2 id="잘한-것이-있다면">잘한 것이 있다면</h2>
<p>책임감을 가지고 마지막까지 임했다고 생각합니다!🙂
비록 깔끔하게 코드를 작성하지 못한 채로 마무리하긴 했으나, 주어진 task를 최대한 끝내고 싶었기 때문에 마지막날 퇴근시간이 지나도 혼자 남아 코드를 쳤던 기억이 남습니다.</p>
<p>그 결과 하나는 실제 서비스에 배포가 되었고, 결과 제출 페이지는 마지막까지 실제 배포를 하진 못 했지만 폼 제출 API만 있다면 사용할 수 있도록 QA까지 진행하여 완성해 두었기 때문에 조만간 완전히 배포될 거 같습니다!</p>
<p>기간은 짧지만 일하고 싶단 생각이 드는 회사에 합류하게 되어 기뻤고, 두 대표님의 열정에 저 또한 자극이 되었던 협업시간이었습니다.
많이 미흡했다고 생각하는데 잘 하고 있다며 칭찬해 주시고😭 좋은 말씀과 조언을 아끼지 않고 해주신 CEO, CTO님께 감사하다는 말씀을 이미 드렸지만..! 여기서도 적어봅니다.</p>
<h2 id="앞으로의-목표">앞으로의 목표</h2>
<p>우선, 적용하지 못한 Context API를 배우고 싶습니다. 배포는 불가능하겠지만 학습을 목적으로 혼자 리팩토링을 진행해보려고 합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[<button>을 클릭했는데 쿼리스트링이 생기는 문제]]></title>
            <link>https://velog.io/@jin_h2_/button%EC%9D%84-%ED%81%B4%EB%A6%AD%ED%96%88%EB%8A%94%EB%8D%B0-%EC%BF%BC%EB%A6%AC%EC%8A%A4%ED%8A%B8%EB%A7%81%EC%9D%B4-%EC%83%9D%EA%B8%B0%EB%8A%94-%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@jin_h2_/button%EC%9D%84-%ED%81%B4%EB%A6%AD%ED%96%88%EB%8A%94%EB%8D%B0-%EC%BF%BC%EB%A6%AC%EC%8A%A4%ED%8A%B8%EB%A7%81%EC%9D%B4-%EC%83%9D%EA%B8%B0%EB%8A%94-%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Fri, 13 Oct 2023 02:25:52 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p>다음 버튼을 눌러야 컴포넌트가 전환되는데
Button을 클릭했을 때 쿼리스트링이 생기면서 컴포넌트가 전환되는 현상이 발생</p>
<h2 id="원인">원인</h2>
<p><code>&lt;form&gt;</code> 태그 안에 있는 <code>&lt;button&gt;</code>은 기본적으로 양식 제출의 기능, <code>type=&quot;submit&quot;</code> 속성을 갖고 있기 때문입니다.
<code>&lt;form&gt;</code>에 method 속성값이 없거나 get일 경우 URL에 폼 데이터가 쿼리 문자열로 추가되고 페이지가 리로드 됩니다.</p>
<h2 id="🎉해결">🎉해결</h2>
<p><code>type=&quot;button&quot;</code> 속성을 부여하거나,
특정 조건 내에서 제출을 막고 싶은 경우라면 onClick 이벤트에 <code>e.preventDfault();</code>를 작성해 줍니다.</p>
<p>저의 경우 type을 지정해 해결했습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[3차 프로젝트 회고 🌳 홍시나무]]></title>
            <link>https://velog.io/@jin_h2_/3%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0-HongC-Tree</link>
            <guid>https://velog.io/@jin_h2_/3%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0-HongC-Tree</guid>
            <pubDate>Thu, 12 Oct 2023 07:19:36 GMT</pubDate>
            <description><![CDATA[<h1 id="💻프로젝트-시작">💻프로젝트 시작</h1>
<ul>
<li>Front-End : <strong>김진희</strong>, 김슬기, 전형민</li>
<li>Back-End : 최홍, 김수형, 김민수, 박건률, 임홍규</li>
<li>진행기간 : 2023/09/04 ~ 2023/09/22</li>
</ul>
<p>지난 2차 프로젝트가 끝나고 곧바로 시작된 3차 프로젝트를 마쳤습니다.
이번엔 팀원들과 Git 컨벤션을 맞추고 Git Rebase로 커밋을 관리해 봤습니다.
카카오에서 제공하는 API도 사용해 본 재밌는 프로젝트였습니다😊</p>
<h2 id="홍시나무-소개">홍시나무 소개</h2>
<h3 id="제품">제품</h3>
<p>대규모 이커머스로 인한 지역경제 상권이 둔화되고 있습니다.
홍시나무는 쉽고 편리한 방식으로 지역 판매자들과 상품을 찾고 구매하도록 도와 지역경제를 활성화시키고, 소비자가 지역 상품에 접근할 수 있는 기회를 제공하기 위해 제작된 서비스입니다.</p>
<h3 id="고객">고객</h3>
<p>우리 동네 상품을 구매하고자 하는 소비자와 지역 주민에게 판매하려는 판매자가 있습니다.
소비자는 실시간으로 언제든 상품을 보고 구매할 수 있어야 하기 때문에 편리하고 빠르게 제품을 보여줘야 합니다.
판매자에게는 정확한 거래 위치를 지정하여 지역 내 거래가 원활히 이뤄질 수 있도록 합니다.</p>
<h3 id="기술">기술</h3>
<p>유저 로그인 인가 시스템을 도입하여 유저 정보에 대한 정확성, 자사의 신뢰도를 인증합니다.
GPS를 사용하여 실시간 사용자의 위치를 확인하여 주변 상품을 검색할 수 있도록 합니다.
또한 카테고리 필터를 세분화하여 편리하고 빠르게 원하는 정보에 접근이 가능합니다.</p>
<p>Front-End : <code>Javascript</code>, <code>React</code>, <code>SCSS</code>
Back-End : <code>Javascript</code>,<code>mysql</code>, <code>node.js</code>
협업툴 : <code>github</code>, <code>slack</code>, <code>trello</code>, <code>notion</code></p>
<hr>
<h2 id="👩💻-내-역할">👩‍💻 내 역할</h2>
<h3 id="ui-기획-설계">UI 기획 설계</h3>
<p>첫 주는 기획을 위한 한 주였습니다.
피그마를 사용해 UI를 기획하였는데, 처음 사용하는데다가 배울 시간이 없어 그림판 사용하듯 했습니다.</p>
<p><img src="https://velog.velcdn.com/images/jin_h2_/post/3de81b7e-93a7-45b6-aeb4-a436209241e7/image.png" alt=""></p>
<ol>
<li>사용자가 주로 모바일을 이용할 것으로 예상, 웹앱으로 제작</li>
</ol>
<hr>
<h3 id="상품리스트---메인화면">상품리스트 - 메인화면</h3>
<blockquote>
<p>판매중인 지역 상품에 간편하게 접근하고, 번거로운 절차를 줄이는 방안을 고민하다가 일정 시간마다 위치를 재설정 해주는 Hook을 사용하여 편리성을 높였습니다.</p>
</blockquote>
<p><code>GeoLocation Hook</code>, <code>GeoCoding</code>을 사용했습니다.
사용자의 사용목적 및 서비스 접근성을 생각하여 로그인이 확인되었을 때 뜨는 메인 화면을 주변 상품을 확인할 수 있는 페이지로 하였습니다.
해당 페이지에서는 현재 내 위치 반경 1km 내 판매중인 상품을 나열해 줍니다.</p>
<img src="https://user-images.githubusercontent.com/93922114/273439957-3047ec40-f01b-4863-b231-789300b517c9.png">

<ol>
<li>GPS 또는 네트워크를 이용해 현재 위치 정보를 반환하는 HTML5 GeoLocation API 사용하여 사용자의 현 위치를 확인</li>
<li>GeoLocation으로 만든 Hook을 이용해 반복적으로 3초마다 현 위치를 확인하여 좌표값 재설정</li>
<li>반경 1km 내 판매중인 상품리스트를 출력</li>
<li>역 지오코딩을 사용하여 좌표값을 주소로 변환, 사용자의 현 위치가 맞는지 직접 확인할 수 있도록 변환한 주소를 상단에 배치</li>
<li>카테고리 세분화 - 신상품 / 중고로 상품의 컨디션과 상품 종류 카테고리를 선택할 수 있도록 하여 소비자가 원하는 상품을 쉽게 찾을 수 있도록 구현</li>
<li>찜하기 - 리스트 페이지에서 상품이 보이지 않더라도 찜한 상품을 보여주는 별도의 페이지에서 확인할 수 있도록 구현</li>
</ol>
<hr>
<h3 id="내-주변-상품보기">내 주변 상품보기</h3>
<blockquote>
<p>이번 프로젝트의 핵심, 카카오맵 API를 사용하여 구현하였습니다.
시각적 표현으로 사용자의 관심을 유도하여 상품 접근성을 높이고자 하였습니다.
또한 나와 가까운 판매 상품을 보여줌으로 보다 빠른 거래가 이루어질 수 있고, 소비자는 상황에 따라 택배나 직거래의 거래 형태를 결정할 수 있도록 도와줍니다.</p>
</blockquote>
<p>사용자의 현 위치와 주변에서 판매중인 상품을 지도로 확인할 수 있습니다.</p>
<img src="https://user-images.githubusercontent.com/93922114/273437810-1d14b791-5442-4de9-9690-3bbde2e343ff.png">

<ol>
<li>현재 유저의 위치와 판매중인 상품을 시각적으로 보여주기 위해 카카오맵 API를 사용하여 지도 위 마커를 띄우는 UI를 구현</li>
<li>GeoLocation API를 사용하여 현 위치를 자동으로 불러오고, 쿼리스트링으로 좌표값을 서버로 전달</li>
<li>좌표값 기준 반경 1km 내 판매중인 상품을 지도 위 마커로 표시</li>
<li>마커 표시된 상품은 메인에서 보이는 상품리스트와 동일</li>
<li>마커 위 hover시 판매상품의 간단한 정보를 볼 수 있고, 클릭 시 상세피이지로 이동</li>
</ol>
<p><strong>😢 아쉬운 점</strong>
카카오맵 리액트 SDK에서는 여러 마커를 띄우는 코드를 샘플로 제공하고 있는데, 이 경우 마커에 인포윈도우를 띄우는 코드가 정해져 있다고 합니다. (카카오에 의하면..?)
여러 마커를 띄우는 것까지는 가능했으나, 해당 마커를 클릭했을 때 상품 정보를 담은 박스가 마커 상단에 뜨는 것을 구현하질 못했습니다.
웹앱으로 만들었는데 hover를 해야 인포윈도우가 뜨는 점은 모바일 사용자 입장에선 큰 불편함을 초래할 것이기 때문에 리팩토링을 진행하며 수정하고자 합니다.</p>
<h3 id="상품-등록-전---지도-위-내-위치-설정">상품 등록 전 - 지도 위 내 위치 설정</h3>
<blockquote>
<p>홍시나무는 지도에 판매상품을 띄워주고 있기 때문에 판매등록 시 해당 상품을 어디서 거래가 가능한 지 입력을 해야합니다.
판매 상품에 판매자의 좌표를 등록하여 사용자가 접속, 검색 시 리스트와 지도에서 확인할 수 있도록 했습니다.</p>
</blockquote>
<img src="https://user-images.githubusercontent.com/93922114/273443894-7aeca33f-2c07-443c-a38e-cc4a1340028a.png">

<ol>
<li>GeoLocation으로 현 위치를 우선 지정, GeoLocation의 정확도 문제와 판매할 장소가 사용자가 있는 곳과 상이할 수 있으므로 사용자가 직접 거래, 판매할 위치를 마커를 옮길 수 있도록 함</li>
<li>마커가 원하는 위치에 정확히 찍혔는지 확인할 수 있도록 역 지오코딩을 사용하여 좌표값을 주소로 변환, 마커 위에 출력</li>
<li>위치 지정 완료 버튼을 누르면 지정한 위치 좌표값을 쿼리스트링으로 상품 등록 페이지에 전달</li>
</ol>
<p>위워크라는 건물을 지정하여 상품을 등록한 후 &#39;내 주변 상품보기&#39;로 이동했을 때 지도에 보여지는 것을 확인할 수 있습니다😊</p>
<hr>
<h3 id="검색">검색</h3>
<blockquote>
<p>입력한 키워드가 한 단어 이상 일치하는 상품이 있으면 리스트로 보여줍니다.
키워드 없이 검색을 진행하려고 한다면 경고문을 띄워줍니다.</p>
</blockquote>
<img src="https://user-images.githubusercontent.com/93922114/273440342-024567fc-4dec-4c65-92c1-fb08097358a0.png">

<ol>
<li>상단의 돋보기 아이콘을 눌러 검색창 모달을 띄움</li>
<li>빈 칸 상태로 검색을 진행 시, 1글자 이상 입력을 요구하는 경고메시지 출력</li>
<li>정상적으로 검색 진행 시, 검색 키워드를 리스트 상단에 띄워 유저가 확인할 수 있고, 키워드에 해당되는 상품을 리스트로 출력</li>
</ol>
<p><strong>😢 아쉬운 점</strong>
어떤 검색어를 입력해야할 지 모르는 사용자를 위해 추천검색어와 같은 가이드를 제공하면 좋을 거 같아 추후 이에 대한 기능을 추가하고 싶습니다.
또한 현재는 검색키워드가 상품명에 있어야 한다는 조건만 갖고 있습니다. 폭넓게 검색할 수 있도록 카테고리와 관련된 검색어를 입력했을 때 해당되는 상품을 보여주는 것도 좋을 거 같다고 생각합니다.
그리고 사용자가 입력한 키워드에 대한 검색결과가 없을 것도 고려하여 검색결과가 없다는 피드백을 주는 것도 필요해 보입니다.</p>
<h2 id="📺시연영상">📺시연영상</h2>
<p><a href="https://www.youtube.com/watch?v=N2UFzhke_48"><img src="https://github.com/serenity1091/images/blob/main/48-3rd-TeamHongC-frontend/%ED%99%8D%EC%8B%9C%EB%82%98%EB%AC%B4%20%EC%8D%B8%EB%84%A4%EC%9D%BC.png?raw=true" alt="홍시나무 시연 영상 썸네일"></a></p>
<h2 id="🏃♀️프로젝트를-진행하며">🏃‍♀️프로젝트를 진행하며</h2>
<h3 id=""></h3>
<ol>
<li><p>rebase
이번 프로젝트는 앞서 서술한 것처럼 Git Rebase로 커밋을 관리했는데, 초반에 이 과정에서 많이 헤맸습니다.
오류도 실수도 잦았고, 이 과정을 해결하는데 많은 시간을 쏟아서 힘들었는데
그래도 팀원들이 먼저 나서서 해결을 도와주려 해서 감사했습니다.</p>
</li>
<li><p>블로그를 쓰질 못했다.
한 번씩 공부한 내용과 문제를 해결한 내용을 정리하며 내재화 시키는 시간을 가졌어야 했는데 그런 노력이 부족했습니다.</p>
</li>
<li><p>부족한 완성도
처음 기획한 기능이 많이 빠지게 되어 아쉽습니다. 기업협업이 끝나면 팀원들과 더 보완하여 완성도를 높이기로 하였습니다.</p>
</li>
<li><p>힘들었지만 새로운 건 재밌다.
프로젝트를 진행하기 앞서 팀원들에게 지도 구현은 제가 하고 싶다고 먼저 말했을 정도로 새로운 도전에 욕심이 있었습니다.
카카오맵 API 사용에 걱정이 없던 것도 아니고, 오랜 시간을 붙잡고도 생각대로 출력이 되지 않아 결국 해결을 못하고 다른 방안으로 대체하였을 만큼 어려움도 많았지만
자바스크립트 기반으로 만들어진 API인데 활용을 잘 못한 것으로 보아선 자바스크립트 공부가 절실히 필요하다고 자기객관화가 되지 않았나 합니다...😅</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Typescript] Call Signatures, 함수 오버로딩, 제네릭]]></title>
            <link>https://velog.io/@jin_h2_/Typescript-Call-Signatures</link>
            <guid>https://velog.io/@jin_h2_/Typescript-Call-Signatures</guid>
            <pubDate>Thu, 12 Oct 2023 06:22:39 GMT</pubDate>
            <description><![CDATA[<h1 id="call-signatures">Call Signatures</h1>
<blockquote>
<p>함수 위에 마우스를 올렸을 때 보게 되는 것.
함수를 구현하기 전에 함수의 매개변수와 반환값의 타입을 만들고 어떻게 작동하는지 미리 서술한다.</p>
</blockquote>
<pre><code class="language-javascript">type Add = (a:number, b:number) =&gt; number;
//함수의 타입을 지정, Call Signatures라고 함.

const add:Add = (a, b) =&gt; a + b</code></pre>
<h1 id="overloading">Overloading</h1>
<blockquote>
<p>함수가 여러 개의 Call Signatures를 가지고 있을 때 발생한다.
외부 라이브러리나 패키지에서 많이 사용됨.</p>
</blockquote>
<pre><code class="language-javascript">type Add = {
  (a: number, b: number) : number
  (a: number, b: string) : number

const add: Add = (a, b) =&gt; {
  if (typeof b === &quot;string&quot;) return a;
  return a + b;
}


type Push = {
  (path:string):void
  (config:Congif):void
}

const push:Push (config) =&gt; {
  if(typeof config === &quot;string&quot;{ console.log(config)) }
  else{
    console.log(config.path)
  }
}</code></pre>
<h1 id="generics-recap">Generics Recap</h1>
<blockquote>
<p>타입을 고정하지 않고, 변수에 따라 타입이 결정되도록 하여 다양한 타입을 사용할수록 있게 한다.
재사용성이 높은 컴포넌트를 만들기에 좋다.
&lt;&gt;를 변수명, 함수명 뒤에 작성해 주어야 하고 제네릭명은 관습적으로 대문자 알파벳 한글자로 처리한다.</p>
</blockquote>
<pre><code class="language-javascript">const generic = &lt;T&gt;(a: T, b: T): T[] =&gt; { ... }

generic(1, 2);
generic(&#39;1&#39;, &#39;2&#39;);

//많은 타입 중 하나가 달라질 수 있는 경우라면 아래와 같이 쓸 수 있음
type User&lt;E&gt; = {
  name: string
  extraInfo: E
}             

type NancyExtra = {
  food:string,
}

type NancyUser = User&lt;NancyExtra&gt;

const nancy: NancyPlayer = {
  name: &quot;nancy&quot;
  extraInfo: {
      favFood : &quot;noodle&quot;
  }
}

// extraInfo가 null인 user
const jessi: Player&lt;null&gt; = {
  name: &quot;jessi&quot;,
  extraInfo: null
}


// 복수의 제네릭을 선언하면 제네릭의 순서를 기반으로 제네릭 타입을 추론
type SuperPrint = &lt;T, M&gt;(a: T[], b: M) =&gt; T

const superPrint: SuperPrint = (a) =&gt; a[0]
const a = superPrint([1, 2, 3, 4])
const b = superPrint([false, true, true, true], &quot;2&quot;)</code></pre>
<hr>
<p>출처
노마드코더 - Typescript
<a href="https://inpa.tistory.com/entry/TS-%F0%9F%93%98-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Generic-%ED%83%80%EC%9E%85-%EC%A0%95%EB%B3%B5%ED%95%98%EA%B8%B">타입스크립트 Generic 타입 정복하기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[EPERM: operation not permitted, rename 에러]]></title>
            <link>https://velog.io/@jin_h2_/EPERM-operation-not-permitted-rename-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@jin_h2_/EPERM-operation-not-permitted-rename-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Wed, 11 Oct 2023 07:21:37 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p>폴더명을 변경하려고 하니 <code>EPERM: operation not permitted, rename</code> 에러 메시지가 발생.</p>
<h2 id="🎉해결">🎉해결</h2>
<p><strong>VSCode를 관리자 권한으로 실행</strong>하니 폴더명을 수정할 수 있었음.</p>
<p>하지만 구글링 해봤을 때
보통 사람들이 작성해 둔 해결 방식은 아래와 같음</p>
<ol>
<li>npm 캐시 제거
<code>npm cache clean --force</code></li>
<li>npm 최신 버전으로 업데이트
<code>npm install -g npm@latest --force</code></li>
<li>npm 캐시 제거
<code>npm cache clean --force</code></li>
</ol>
<p>이후로 다시 vscode를 열어주면 됨.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] \r\n(＼r＼n) 줄바꿈(개행)이 안될 때, CSS white-space]]></title>
            <link>https://velog.io/@jin_h2_/React-rn%EF%BC%BCr%EF%BC%BCn-%EC%A4%84%EB%B0%94%EA%BF%88%EA%B0%9C%ED%96%89%EC%9D%B4-%EC%95%88%EB%90%A0-%EB%95%8C-CSS-white-space-%EC%86%8D</link>
            <guid>https://velog.io/@jin_h2_/React-rn%EF%BC%BCr%EF%BC%BCn-%EC%A4%84%EB%B0%94%EA%BF%88%EA%B0%9C%ED%96%89%EC%9D%B4-%EC%95%88%EB%90%A0-%EB%95%8C-CSS-white-space-%EC%86%8D</guid>
            <pubDate>Thu, 05 Oct 2023 08:40:25 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p>기업 인턴십을 진행하며
상수데이터를 불러오려고 하는데 개행이 되지 않아 찾아보게 되었습니다.
우선, 운영체제에 따라 개행 방법이 다르다는 것.</p>
<p><code>Window</code> : \r\n
<code>Unix</code> : \n
<code>Mac OS X</code> : \r
<code>Macintosh</code> : \n</p>
<p>저는 윈도우를 사용중이니 아래처럼 코드를 수정했습니다.</p>
<pre><code class="language-javascript">export const SERVICEINFO = {
    &#39;1인 3시간 서비스&#39;: {
        imageUrl: &#39;&#39;,
        description: &#39;👉 평균 행거 4개, 서랍장 1개 정리\r\n👉 또는 행거 1개, 서랍장 2개\r\n         👉 또는 행거 6개&#39;,
    },
    &#39;1인 4시간 서비스&#39;: {
        imageUrl: &#39;&#39;,
        description: &#39;👉 평균 행거 4개, 서랍장 1개, 리빙박스 2개 정리\r\n👉 또는 행거 6개, 서랍장 1개\r\n         👉 또는 행거 8개&#39;,
    },
    &#39;1인 5시간 서비스&#39;: {
        imageUrl: &#39;&#39;,
        description: &#39;👉 평균 행거 4개, 서랍장 2개, 리빙박스 4개 정리\r\n👉 또는 행거 6개, 서랍장 2개\r\n         👉 또는 행거 10개&#39;,
    },
    &#39;2인 3시간 서비스&#39;: {
        imageUrl: &#39;&#39;,
        description: &#39;👉 평균 행거 10개, 서랍장 1개 정리\r\n👉 또는 행거 2개, 서랍장 4개\r\n         👉 또는 드레스룸 1개&#39;,
    },
    &#39;2인 4시간 서비스&#39;: {
        imageUrl: &#39;&#39;,
        description: &#39;👉 평균 드레스룸 1개, 행거 4개 정리\r\n👉 또는 행거 8개, 서랍장 2개, 리빙박스 4개&#39;,
    },
    &#39;2인 5시간 서비스&#39;: {
        imageUrl: &#39;&#39;,
        description: &#39;👉 평균 드레스룸 1개, 행거 4개, 서랍장 2개 정리\r\n👉 또는 행거 10개, 서랍장 3개, 리빙박스 4개&#39;,
    },
    &#39;2인 6시간 서비스&#39;: {
        imageUrl: &#39;&#39;,
        description: &#39;👉 평균 드레스룸 2개 정리\r\n👉 또는 드레스룸 1개, 행거 12개&#39;,
    },
};
</code></pre>
<img src="https://velog.velcdn.com/images/jin_h2_/post/b99d3553-e8ce-403c-9c86-4f7727075225/image.png" width="350px">
하지만 위처럼 개행이 되지 않았는데요😢

<h2 id="원인">원인</h2>
<p>원인은 CSS <code>wthie-space</code> 속성이 기본적으로 normal로 적용되어 있고,
연속된 띄어쓰기, 들여쓰기, 줄바꿈 문자가 무시되기 때문입니다.</p>
<h2 id="해결🎉">해결🎉</h2>
<p>그렇기 때문에 서비스 내용을 감싸주고 있는 <code>div</code>에 <code>white-space: pre-wrap;</code>를 넣어주면 해결됩니다!</p>
<img src="https://velog.velcdn.com/images/jin_h2_/post/94def79e-23e4-4691-9b87-8b93c987203f/image.png" width="350px">

<h1 id="✏️-white-space">✏️ white-space</h1>
<p><code>white-space</code>는 공백을 어떻게 처리할 지를 정의해 줍니다.</p>
<p><strong>1) nomal</strong>
연속된 공백을 하나로 합쳐주고 개행문자도 다른 공백 문자와 동일하게 처리합니다
줄이 너무 길어지면 자동으로 줄을 바꿔줍니다.</p>
<p><strong>2) nowrap</strong>
연속된 공백을 하나로 합치고 <br>로 줄바꿈이 가능합니다.</p>
<p><strong>3) pre</strong>
연속된 공백을 유지하고 개행문자와 <br>로 줄바꿈이 가능합니다.
줄이 길어져도 줄바꿈이 되지 않습니다.</p>
<p><strong>4) pre-wrap</strong>
연속된 공백을 적용하고 개행문자와 <br>로 줄바꿈 가능합니다.
줄이 너무 길어지면 자동으로 줄을 바꿔줍니다.</p>
<p><strong>5) break-spaces</strong>
pre-wrap과 동일하나,
연속 공백이 줄의 끝에 위치하더라도 공간을 차지하고
연속 공백의 중간과 끝에서도 자동으로 줄을 바꿀 수 있습니다.
유지한 연속 공백은 pre-wrqp과 달리 바깥으로 넘치지 않고,
공간을 차지하기 때문에 박스의 본질 크기에 영향을 줍니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] API KEY 같이 유출되면 안되는 값은 .env로!]]></title>
            <link>https://velog.io/@jin_h2_/React-API-KEY-%EA%B0%99%EC%9D%B4-%EC%9C%A0%EC%B6%9C%EB%90%98%EB%A9%B4-%EC%95%88%EB%90%98%EB%8A%94-%EA%B0%92%EC%9D%80-.env%EB%A1%9C</link>
            <guid>https://velog.io/@jin_h2_/React-API-KEY-%EA%B0%99%EC%9D%B4-%EC%9C%A0%EC%B6%9C%EB%90%98%EB%A9%B4-%EC%95%88%EB%90%98%EB%8A%94-%EA%B0%92%EC%9D%80-.env%EB%A1%9C</guid>
            <pubDate>Sat, 09 Sep 2023 14:57:10 GMT</pubDate>
            <description><![CDATA[<p>새 프로젝트에서 지도API를 사용하게 되었습니다.</p>
<p>API 키 도난은 개인정보 유출로 이어지기 때문에 보안에 신경을 써야합니다.
오늘 배우게 된 내용은 제공된 API 값이 외부에 유출되지 않도록 .env 파일에 환경변수를 선언하여 사용하는 방법입니다. 이는 유지보수에도 용이합니다👍</p>
<h1 id="env">.env</h1>
<ol>
<li><p>최상위 폴더에 <code>.env</code> 파일을 생성합니다.
<img src="https://velog.velcdn.com/images/jin_h2_/post/ecda9a34-b237-41ef-9652-6b415daa3e12/image.png" alt=""></p>
</li>
<li><p><code>.env</code> 파일에 작성될 변수명은 <code>REACT_APP_</code>으로 시작합니다.
예를 들면 <code>REACT_APP_KAKAO_MAP_KEY = 1234abiowelk</code>
그렇지 않으면 리액트가 인식을 하지 못합니다.</p>
</li>
<li><p>push해서 유출되면 안되니까 <code>.gitignore</code> 파일에 <code>.env</code>를 추가해 주세요.</p>
</li>
</ol>
<h1 id="사용하기">사용하기</h1>
<h2 id="1-자바스크립트-파일에서-사용">1. 자바스크립트 파일에서 사용</h2>
<pre><code class="language-javascript">process.env.REACT_APP_나머지변수명</code></pre>
<p><code>{process.env.REACT_APP_나머지변수명}</code>으로 사용하거나 다른 변수명에 할당하여 사용하면 됩니다.</p>
<h2 id="2-html-파일에서-사용">2. .html 파일에서 사용</h2>
<p>index.html에서 사용할 경우에는 % 사이에 변수명을 넣어야 합니다.
<code>%REACT_APP_나머지변수명%</code></p>
<p>예로 작성하면 아래와 같겠습니다.</p>
<pre><code class="language-javascript">&lt;script type=&quot;text/javascript&quot; src=&quot;//dapi.kakao.com/v2/maps/sdk.js?appkey=%REACT_APP_KAKAO_MAP_KEY%&quot;&gt;&lt;/script&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[2차 프로젝트 회고 🎬 Elbow Cinema]]></title>
            <link>https://velog.io/@jin_h2_/2%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0-Elbow-Cinema</link>
            <guid>https://velog.io/@jin_h2_/2%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0-Elbow-Cinema</guid>
            <pubDate>Mon, 04 Sep 2023 15:03:11 GMT</pubDate>
            <description><![CDATA[<h1 id="프로젝트-시작💻">프로젝트 시작💻</h1>
<ul>
<li>Front-End : <strong>김진희</strong>, 한지선</li>
<li>Back-End : 김수영, 전민성, 정동민 </li>
</ul>
<p>본격적으로 프론트엔드 2명과 백엔드 3명이 한 팀이 되어 진행된 2주간의 프로젝트가 마무리 되었습니다.</p>
<p>영화 예매 사이트를 제작하였고, 제작하기 앞서 타 사이트를 벤치마킹하여 Product 분석을 먼저 진행했습니다.</p>
<h2 id="👉-분석-및-설계">👉 분석 및 설계</h2>
<h3 id="제품">제품</h3>
<p>해당 서비스를 통해 현재 관람이 가능한 영화와 해당 영화에 대한 다양한 정보를 토대로 원하는 영화를 예약할 수 있습니다.
메인 및 리스트 페이지에서는 최소한의 정보를 제공하고 자세한 정보는 상세 페이지를 통해 확인할 수 있습니다.
제공되는 정보가 많고 예약이 다소 복잡하게 느껴질 수도 있어 고객이 영화를 선택하는 과정에 가장 많이 참고할 정보를 영화 리스트 페이지(메인)에 노출시키고, UI는 직관성을 높이고자 최대한 복잡함을 덜었습니다.</p>
<h3 id="고객">고객</h3>
<p>남녀노소 즐기는 문화생활이므로 예매 과정이 복잡하면 안됩니다.
인터넷 사용이 어려울 수 있는 연령대까지 고려한 간결한 UI, 복잡한 회원가입 절차를 생략하고 비회원 예매와 소셜 로그인이 가능하면 좋을 것으로 보였습니다.
다만 미성년자 관람불가 영화의 경우, 법적으로 보호자 동반하에도 관람이 불가능하기 때문에 신원 확인이 된 유저 정보가 있어야 예매가 되도록 합니다.
예약이 완료되면 간단한 결제 및 예약 정보를 &#39;마이티켓&#39; 페이지에서 시간순으로 확인할 수 있도록 합니다.</p>
<h3 id="기술">기술</h3>
<p>Front-End : <code>Javacript</code>, <code>React</code>, <code>SCSS</code>
Back-End : <code>Javascript</code>, <code>mysql</code>, <code>node.js</code>
협업툴 : <code>github</code>, <code>slack</code>, <code>trello</code>, <code>notion</code></p>
<hr>
<h2 id="👉-내가-맡은-파트는">👉 내가 맡은 파트는?</h2>
<h3 id="1-회원가입--로그인">1. 회원가입 / 로그인</h3>
<h4 id="회원가입">회원가입</h4>
<p><img src="https://velog.velcdn.com/images/jin_h2_/post/be9b6619-e60f-41a3-b886-67fa3aaf80f7/image.png" alt="">
<img src="https://velog.velcdn.com/images/jin_h2_/post/05c4f824-c1fb-4f90-b0d2-cc0acd6dc78a/image.png" alt=""></p>
<ol>
<li><p>작성해야할 회원가입 정보를 최소화하여 가입 이탈율을 낮춤
＊성별, 생년월일은 영화별 선호 연령층과 성별 데이터 수집을 위한 목적</p>
</li>
<li><p>유효성 검사
▶ 정규 표현식을 사용하여 코드를 간결화, 이에 대한 실시간 안내 메시지를 출력하여 요구하는 올바른 정보 기입을 유도</p>
</li>
<li><p>조건 충족시 가입 버튼 활성화
▶ 가입신청 &gt; 불충족으로 인한 경고메시지의 과정을 간소화하여 가입시 발생할 수 있는 과정의 불편함을 해소</p>
</li>
<li><p>계정 중복 가입시 경고창 출력
▶ 이미 가입된 아이디와 이메일이 중복 입력될 경우 경고문을 출력</p>
</li>
</ol>
<h4 id="로그인">로그인</h4>
<p><img src="https://velog.velcdn.com/images/jin_h2_/post/b8e70b40-937e-4462-94c4-1a60ff19ad0a/image.png" alt=""></p>
<ol>
<li><p>아이디, 비밀번호 미일치시 경고창 출력
▶ 서버에 저장된 아이디와 패스워드, 그리고 토큰 유무를 판단</p>
</li>
<li><p>예매 과정 중 로그인이 필요한 경우, 로그인 요청 경고문을 출력하고 해당 페이지로 이동</p>
</li>
<li><p>2번 과정에서 유입된 비회원이 회원가입 페이지로 빠른 접속을 할 수 있도록 로그인 하단에 회원가입 버튼 배치 </p>
<br>

</li>
</ol>
<h3 id="2-영화예매">2. 영화예매</h3>
<p>쿼리스트링으로 선택한 영화의 데이터를 전달, 불러오도록 하였습니다.
<code>useSearchparams</code> hook을 이용하였고, 이에 따라 선택 도중 다른 페이지를 이동하더라도 선택값이 남게 됩니다.
<img src="https://velog.velcdn.com/images/jin_h2_/post/c77f82b7-8fb9-46c3-a49d-7f9df3a21bc2/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jin_h2_/post/9fcd3475-4a49-4df6-a8d3-03d7f274d5e3/image.png" alt=""></p>
<ol>
<li><p>직전 페이지에서 영화를 선택, 예매버튼을 눌렀을 경우 바로 예약 가능 날짜 출력하여 예매 과정을 최소화</p>
</li>
<li><p>재선택을 원할 경우 다시 선택 버튼을 눌러 상태를 초기화</p>
</li>
<li><p>이벤트 핸들러 함수를 이용하여 사용자 선택에 따른 상태 업데이트, 데이터 출력
▶ 예매를 희망하는 영화의 예매 가능 시간, 좌석 정보를 동적으로 출력하여 결제까지의 진행 과정을 유도</p>
</li>
<li><p>쿼리스트링을 이용하여 선택한 데이터를 다음 페이지(좌석선택)로 전달하여 통신 과정을 줄임</p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/jin_h2_/post/46397af5-54d5-42c8-bbbe-50629d91dfc2/image.png" alt=""></p>
<ol start="5">
<li>영화 목록을 예매순, 가나다순으로 정렬 : 편리한 영화 검색을 위해 고객이 영화를 찾을 방법중에서 가장 보편적인 기준으로 예상되는 예매순, 가나다순의 sorting 기능 제공.</li>
</ol>
<br>

<h3 id="3-결제창">3. 결제창</h3>
<p><img src="https://velog.velcdn.com/images/jin_h2_/post/083324f4-4857-49ab-a996-db7d3e9326d6/image.png" alt=""><img src="https://velog.velcdn.com/images/jin_h2_/post/fe09440b-e16f-407d-8f37-159c66e35a10/image.png" alt=""></p>
<ol>
<li><p>보유 포인트로 결제 가능
▶ 토큰으로 고객 정보를 확인, 해당 고객이 보유한 포인트를 사용하여 결제</p>
</li>
<li><p>예매에 필요한 결제 금액을 표시하고 내가 입력한 포인트를 실시간으로 출력
▶ 포인트 외 다수의 결제 방법을 이용할 경우, 고객이 입력한 금액과 영화티켓 총액이 일치하는지를 확인 가능</p>
</li>
<li><p>보유 포인트가 입력 포인트보다 클 경우 경고메시지 출력
▶ 보유한 포인트 내에서 올바른 입력을 유도</p>
</li>
<li><p>입력 금액과 결제가 필요한 총액이 일치할 경우 결제 버튼 활성화
▶ 불일치 시 다음 과정으로 넘어가면서 결제 재선택을 요구하는 불필요한 과정을 축소</p>
</li>
<li><p>결제된 금액(포인트)를 서버로 전달하여 사용한 포인트를 보유 포인트에서 차감</p>
</li>
</ol>
<h3 id="4-그-외">4. 그 외</h3>
<p>자주 사용하는 스타일 속성을 mixin으로 정리, Footer UI 제작</p>
<h3 id="시연영상">시연영상</h3>
<p>!youtube[asCDl-F5L0M?si=0bAqzX99NlJ1Enl3]</p>
<hr>
<h2 id="👉-프로젝트를-진행하며">👉 프로젝트를 진행하며</h2>
<h3 id="팀원간-소통-및-진행과정-공유">팀원간 소통 및 진행과정 공유</h3>
<ol>
<li><p>스탠드업 미팅
팀원 한 분도 빠짐없이 매일 정해진 시간에 각자의 진행과정과bloking, 다음 목표를 공유하는 시간을 가졌습니다.</p>
</li>
<li><p><code>Trello</code>
<code>Trello</code>에서 각자의 진행 과정을 관리하였습니다.
스프린트 계획은 필수 구현과 선택 구현 사항, 그리고 각 페이지별 UI와 기능 구현으로 티켓을 나눴는데
저의 경우 UI 제작은 1~2일 소요되었고 mockdata를 이용하여 기능을 구현하는 기간은 3일 정도 걸렸습니다.</p>
</li>
</ol>
<p>여기서 _아쉬웠던 점_은,
mockdata를 이용한 기능 구현의 경우는 내가 소화 가능한 일정이 그려졌지만
리뷰에 따른 코드 수정과 통신 오류로 인한 수정 기간은 예상이 되지 않아 효율적인 시간 관리가 되지 않았습니다.
때문에 진행하면서 후순위의 스프린트 기간이 짧아지게 되고, 완성도가 떨어졌습니다.
이는 후술하겠지만 소통의 문제가 큰 이유였을 것이라 보기 때문에 백엔드 분들과 좀 더 긴밀한 소통을 해야겠다고 다짐했습니다.</p>
<h3 id="기억하고-싶은-코드--해결한-문제">기억하고 싶은 코드 / 해결한 문제</h3>
<p>이번 프로젝트에서는 <code>useSearchparams</code>를 예매페이지에 적용해보고 싶었습니다. 그런데 이로 인한 꽤나 시간 지체가 있었습니다.
이유는 제가 <strong><em>사용법을 제대로 숙지하지 못했다</em></strong>는 것.
그로 인해 팀원이 <code>useParams</code>를 사용하여 받는 것으로 코드를 짰는데, 이것이 문제가 되었다는 것을 빠르게 알아채지 못해 시간을 허비하게 되었습니다.
같은 이유로 백엔드와의 통신 과정에 문제가 있기도 했고요.
새로운 것을 적용해보고자 한다면 제대로 숙지한 후에 논의해야겠습니다...😢</p>
<h3 id="느낀-점">느낀 점</h3>
<ol>
<li><p>😔 소통의 아쉬움
프론트엔드끼리의 합의도 그렇지만, 우려했던대로 혼자 기능 구현을 할 때까진 원활히 진행되다가 백엔드와의 통신 과정에서 일어난 오류를 잡는 시간이 너무 길었던 거 같습니다.
데이터를 맞춰야 할 부분에서 소통없이 진행하는 바람에 코드를 수정, 또 수정하는 일이 있었던 점이 아쉬웠습니다.
문서화 작업이 미흡하기도 했고, 저 역시나 필요한 정보나 건의사항이 있으면 빠르게 전달했었어야 했는데 이 부분에 있어서는 너무 소극적이었단 생각이 듭니다.
사실 팀원 모두 서로의 언어를 모르다보니 발생한 것인데, 서로의 방식을 알고자 하는 마인드도 중요한 거 같습니다.
다음 프로젝트는 더 원활히 진행되도록 어떤 상황에 어떤 대화가 오가야 하는지 깨닫게 된 부분과 모르면 모른다고 말하고, 배우려고 하는 마음가짐을 새 프로젝트 진행 전에 다시 상기해야겠습니다.</p>
</li>
<li><p>😔 시간관리의 중요성
선택사항 구현까지도 충분히 가능하겠다고 자신만만했는데 너무나도 큰 오산이었던 것 입니다..
앞서 작성한 내용이지만 예상 외의 문제들을 해결하느라 지체된 시간으로 인해 결국 완성도가 떨어졌습니다. (소통을 안 했으니..)
점점 쫓기는 시간에 어떻게 해서든 기본적인 UI와 동작이 가능하도록 제작하는 데에 몰두하다보니 내가 작성하는 코드에 대한 이해도도 떨어지게 되고.. 심적인 부담이 더 커질 수 밖에 없었습니다.
주어진 티켓 중 선택사항이지만 추가로 구현하고자 했던 기능을 넣었어야 분석을 토대로 설계한, 비로소 고객이 불편함없이 쓸 수 있는 예매사이트에 가까웠을텐데 우리 팀원분들에게 죄송한 마음이 들었습니다😢
다음부턴 꼭꼭 시선을 조금 더 내다보아서 완성도를 높일 수 있는 일정을 계획할 수 있도록 해야겠습니다.</p>
</li>
<li><p>😔 컴포넌트 분리
프론트엔드 2명, 백엔드 3명 구성에서 오는 압박감에 빠른 시작이 중요하겠다 싶어 컴포넌트로 만들어 사용할 수 있는 코드(예로 버튼)도 서로 제각각 만들어서 사용해 마무리 했던 점이 아쉽게 느껴집니다.</p>
</li>
<li><p>😀 팀 분위기와 열정
팀원 모두 부담감도 크고 오류를 잡는 과정에 스트레스가 컸을텐데 그럼에도 불구하고 화기애애한 분위기 속에서 프로젝트를 잘 마무리했다고 생각합니다.
백, 프론트 서로의 언어는 모르더라도 함께 blocking을 파악하고 해결하고자 노력해 주셔서 결과물이 나온 거 같습니다.
함께 해결하고 나면 어느때보다 뿌듯함이 느껴지고 의지가 됩니다😊</p>
</li>
</ol>
<hr>
<p>마지막즈음엔 주말도 없이, 모여서 새벽까지 함께 해서 정신적으로나 체력적으로 힘들긴 했지만 서로 짠 코드들이 맞물려서 하나의 서비스가 되는 것을 눈으로 확인하니 신기하기도 하고 재밌었습니다😊
문서화 작업부터 해서 체계적으로 진행했으면 조금 더 수월하게 프로젝트를 마무리 할 수 있었을 거 같단 생각이 듭니다.
아직 더 구현할 것들이 많아보여서 추후 진행할 프로젝트를 마무리 하고 추가적으로 더 진행하고자 합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[1차 프로젝트 회고]]></title>
            <link>https://velog.io/@jin_h2_/1%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@jin_h2_/1%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sat, 19 Aug 2023 08:48:56 GMT</pubDate>
            <description><![CDATA[<h1 id="1차-미니-프로젝트">1차 미니 프로젝트</h1>
<p>1주간 진행된 미니 프로젝트를 마치고 회고록을 작성해 봅니다.
런칭 4시간만에 500만 명의 가입자 수를 달성했다고 하는 트위터의 대항마로 떠오르는 스레드의 일부를 클론하는 프로젝트였습니다.
구현해야했던 페이지는 크게 회원가입/로그인, 스레드 리스트, 글 작성 페이지였습니다.</p>
<p><img src="https://velog.velcdn.com/images/jin_h2_/post/ac960615-05b2-4d0d-b0b2-b370746308ca/image.png" alt=""></p>
<h2 id="사용된-기술">사용된 기술</h2>
<p><code>html</code>, <code>css</code>, <code>react</code>, <code>scss</code>, 소통은 <code>slack</code>과 <code>trello</code>로.</p>
<h2 id="내가-맡은-역할">내가 맡은 역할</h2>
<p>초기세팅을 진행, 글 작성 및 수정페이지 구현과 Project Manager를 맡게 되었습니다.</p>
<h3 id="1-pm">1) PM</h3>
<p>Trello를 사용하여 팀원별 역할 배분 및 진행 여부를 파악하고 Daily Standup Meeting을 진행하여 소통할 수 있는 시간을 가져 최대한 기한 내 목표를 달성할 수 있도록 하였습니다.</p>
<h3 id="2-front-end">2) Front-End</h3>
<p>글쓰기 페이지에서 취소 버튼을 클릭 시 리스트로 이동하고,
게시 버튼을 클릭하면 textarea에 한 글자 이상 입력되었는지를 확인하여
게시 여부를 판단하도록 하였습니다.
만약 빈 칸으로 게시 버튼을 눌렀다면 alert 창이 뜨게 되고,
내용을 채워 게시 버튼을 누르면 서버로 내용을 전달하고 페이지는 스레드 리스트 페이지로 이동하게 됩니다.</p>
<h2 id="기억나는-코드">기억나는 코드</h2>
<p>팀원이 로그인 과정에서 토큰을 불러오지 못하는 상황이었고, 찾은 원인은 fetch 함수를 화살표 함수로 사용, 여기서 중괄호가 쓰였으나 <code>return</code>이 없었던 것이었습니다.
저와 팀원 둘이서 코드를 확인하고 있었음에도 찾아내지 못했는데,화살표 함수 문법을 제대로 숙지하고 있지 못하여 발생한 문제인 만큼 다시 한 번 공부하는 시간을 가져야겠습니다.</p>
<pre><code>// 일반적인 화살표 함수 표현
elements.map((element) =&gt; {
      return element.length;
    });

// 화살표 함수 문장이 return뿐일 때 return과 중괄호 생략 가능
elements.map(element =&gt; element.length);</code></pre><h2 id="잘했던-점">잘했던 점</h2>
<ul>
<li>팀원 모두 기한을 지켜 목표를 달성하였습니다.</li>
<li>미팅 진행 전, 각자의 진행 과정을 미리 정리하여 미팅 시간을 줄이고자 하였습니다.</li>
<li>어려운 것이 있으면 공유하여 빠르게 해결하고자 하였습니다.</li>
</ul>
<h2 id="아쉬웠던-점">아쉬웠던 점</h2>
<ul>
<li>trello 업데이트가 늦어져 진행사항 확인이 원활하지 못했습니다.</li>
<li>프로젝트 기간이 짧다보니 각자의 업무를 진행하느라 긴밀한 소통을 하지 못한 것 같고, 하나의 프로덕트로 만들지 못해 아쉬웠습니다. </li>
<li>백엔드와의 협업을 제대로 진행하지 못하고, 각자 흩어져 통신이 되었는지 여부를 확인한 점이 아쉽습니다.</li>
<li>컨벤션을 숙지하지 못하여 프로젝트 진행 도중 수정한 일이 몇 번 있었습니다.</li>
</ul>
<h2 id="개선해야할-점과-느낀-점">개선해야할 점과 느낀 점</h2>
<ul>
<li>trello 관리, 소통 면에서 좀 더 PM 역할을 제대로 하지 못한 듯 하여 리더의 역할이 무엇인지를 다시 되짚어 봐야겠단 생각이 들었습니다.</li>
<li>컨벤션을 숙지하고 팀원들과 미리 정해서 두 번 작업하는 고생을 덜어야겠습니다.</li>
<li>게시불가능인 상황에 버튼이 비활성화 되는 코드를 추가했으면 좋았을 거 같아 추가적으로 작업해보려고 합니다.</li>
<li>조금 더 열성적인 자세로 임하는 팀장이고 싶었으나 그러지 못했던 것이 마음에 걸려 조금 더 주변에 관심을 갖고 적극적으로 팀을 이끌어야겠다고 반성합니다😢 단호함도 필요한 거 같습니다.</li>
</ul>
<p>짧게 끝나버린 미니 프로젝트이다보니 아쉬움도 많고, 제대로 마무리를 짓지 못하여 찝찝함이 많이 남습니다.</p>
<p>앞으로 남은 프로젝트도 있으니 1차에서 배우고 느낀 것을 잊지 않고 남은 프로젝트를 성공적으로 마치도록 노력하겠습니다✨</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[display:flex로 하단 영역 배치하기]]></title>
            <link>https://velog.io/@jin_h2_/displayflex%EB%A1%9C-%ED%95%98%EB%8B%A8-%EC%98%81%EC%97%AD-%EB%B0%B0%EC%B9%98%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jin_h2_/displayflex%EB%A1%9C-%ED%95%98%EB%8B%A8-%EC%98%81%EC%97%AD-%EB%B0%B0%EC%B9%98%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 14 Aug 2023 09:31:46 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<img src="https://velog.velcdn.com/images/jin_h2_/post/43749e7f-88ab-4583-9a7c-f87aed0cd761/image.png" width="500px">

<p>쓰레드 클론코딩 과정 중 위와 같은 쓰레드를 남기는 페이지를 구현하고자 하였는데
<code>position: absolute</code> css 속성을 이용하여 배치를 했더니
버튼 영역이 <code>textarea</code> 영역을 가린다.
구현하고자 한 화면이 웹페이지가 아니어서 브라우저 가운데에 위치하도록 설정해두었기 때문에 <code>position: fixed</code>, <code>bottom:0</code>을 하면 원하는 레이아웃 화면이 절대 나올 수 없었다.</p>
<img src="https://velog.velcdn.com/images/jin_h2_/post/80e29878-6f49-4712-a6a7-baed18a071f5/image.png" width="500px">

<p>그래서 고민 끝에 <code>textarea</code>에 padding값을 크게 주어 외관상의 문제를 어떻게 해결해 보았지만 아무리 이게 맞나 싶고,
<code>button</code> 영역과 <code>textarea</code> 영역을 분리할 방법을 찾아야 했다.</p>
<h2 id="해결🎉">해결🎉</h2>
<p><code>position</code> 속성을 지우고 분리하고 싶은 영역의 부모에 <code>display: flex</code>, <code>flex-direction: column</code>을 주니 깔끔하게 분리가 되었다.
다른 페이지에서 나와 같은 이유로 레이아웃 구성을 고민하던 팀원이 찾은 방법을 공유받았다.
아래가 최종 결과물!
<img src="https://velog.velcdn.com/images/jin_h2_/post/e45863af-7623-4267-808f-402b247da3b3/image.png" width="500px"></p>
<h3 id="왜-처음부터-못했는가😢">왜 처음부터 못했는가😢</h3>
<p>퍼블리싱을 배우고서 <code>flex</code>를 쓴 경험이 없었고, 사용해야 할 이유를 인지하지 못하고 있었다보니
습관상 당연하게 <code>position</code>을 이용하여 해결하려고 했다.
아직도 레이아웃 구성을 하는데 <code>display: flex</code>를 쓸 생각을 바로 떠올리지 못했다니😂
속성에 대해서도 완벽하게 숙지를 하지 못한듯하여 좀 더 익숙해질 수 있도록 많이 연습해 봐야겠다...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] JSX 문법 8가지]]></title>
            <link>https://velog.io/@jin_h2_/React-JSX-%EB%AC%B8%EB%B2%95-8%EA%B0%80</link>
            <guid>https://velog.io/@jin_h2_/React-JSX-%EB%AC%B8%EB%B2%95-8%EA%B0%80</guid>
            <pubDate>Sat, 05 Aug 2023 08:14:39 GMT</pubDate>
            <description><![CDATA[<h1 id="jsxjavascript-syntax-extension">JSX(JavaScript Syntax eXtension)</h1>
<blockquote>
<p>JSX는 리액트에서 사용하는 자바스크립트 확장 문법입니다.</p>
</blockquote>
<p>기존 바닐라 JS는 HTML에서 마크업된 부분을 확인하면서 해당 DOM에 접근하고 Event Listener를 부착하는 등 HTML과 JS는 긴밀하게 연결되어 있습니다.
반면 <strong>JSX는 HTML과 JS 로직을 하나의 JS 파일 안에서 모두 처리</strong>합니다.
작성한 코드는 브라우저에서 동작하기 전에 Babel이라는 transcompiler를 통해 일반 JS 코드 형태로 변환됩니다.</p>
<p><img src="https://velog.velcdn.com/images/jin_h2_/post/c8ce8355-ac6c-4471-b3aa-0e20e0dd434d/image.png" alt=""></p>
<h2 id="✒️-jsx-문법">✒️ JSX 문법</h2>
<h3 id="span-stylebackground-color-fffbdc1-jsx-elementspan"><span style='background-color: #fffbdc'>1) JSX element</span></h3>
<p>HTML처럼 작성이 가능합니다.</p>
<pre><code>const hi = &lt;p&gt;Hi&lt;/p&gt;;</code></pre><p>변수에 저장하거나 함수의 인자로 넘길 수도 있습니다.</p>
<h3 id="span-stylebackground-color-fffbdc2-js-표현식span"><span style='background-color: #fffbdc'>2) JS 표현식</span></h3>
<p>JSX 내부에서 JS 값을 출력하고 싶다면
{ ...Javascript... }와 같이 {} 안에 유효한 JS 표현식을 작성할 수 있습니다.</p>
<pre><code class="language-javascript">// Greetings.js

import React from &#39;react&#39;

const Greating = () =&gt; {
    const name = &#39;김개발&#39;;

    return &lt;h1&gt;{name}님, Welcome to Wecode!&lt;/h1&gt;;
}

export default Greetings;</code></pre>
<h3 id="span-stylebackground-color-fffbdc3-jsx-attribute속성span"><span style='background-color: #fffbdc'>3) JSX attribute(속성)</span></h3>
<p>태그의 속성명은 camelCase로 작성해야 합니다.
attribute를 추가하고 싶을 때는 HTML에서 쓰는 속성명과 다를 수 있기 때문에 <a href="https://ko.legacy.reactjs.org/docs/dom-elements.html#all-supported-html-attributes">공식문서</a>를 참고하세요.</p>
<pre><code class="language-html">// HTML
&lt;h1 class=&quot;greetings&quot;&gt;Welcome to Wecode!&lt;/h1&gt;

// JSX
&lt;h1 className=&quot;greetings&quot;&gt;Welcome to Wecode!&lt;/h1&gt;</code></pre>
<p>속성명이 <code>class</code>가 아닌 <code>className</code>을 사용해야 합니다.</p>
<h3 id="span-stylebackground-color-fffbdc4-event-처리하기span"><span style='background-color: #fffbdc'>4) Event 처리하기</span></h3>
<p>기존 바닐라 JS에서는 해당하는 DOM 요소에 직접 접근하여 이벤트리스너를 부착하는 방식으로 이벤트를 처리했다면
JSX에서는 태그를 작성할 때 이벤트와 이벤트 핸들러(Event Handler)를 부여할 수 있습니다.</p>
<pre><code class="language-javascript">// JS
const title = document.getElementsByClassName(&#39;title&#39;)[0];
title.addEventListener(&#39;click&#39;, handleClick);

// JSX
&lt;h1 className=&quot;title&quot; onClick={handleClick}&gt;Welcome to Wecode!&lt;/h1&gt;</code></pre>
<ul>
<li>이벤트 앞에 <code>on</code>을 붙여 camelCase로 작성</li>
<li>문자열이 아닌 함수로 이벤트 핸들러 전달</li>
</ul>
<h3 id="span-stylebackground-color-fffbdc5-inline-stylingspan"><span style='background-color: #fffbdc'>5) Inline Styling</span></h3>
<p>style 속성은 camelCase를 요소로 가지는 JS 객체를 받기 떄문에 중괄호를 두 번 겹쳐서 쓰는 형태로 사용합니다.
바깥의 <code>{}</code>는 JSX 문법을 의미하고 안쪽의 <code>{}</code>는 JS 객체를 의미합니다.</p>
<pre><code>// HTML
&lt;h1 style=&quot;color:red;background-image:yellow&quot;&gt;Welcome to Wecode!&lt;/h1&gt;

// JSX
&lt;h1 style={{ color: &quot;red&quot;, backgroundImage: &quot;yellow&quot; }}&gt;
  Welcome to Wecode!
&lt;/h1&gt;</code></pre><p><span style="font-size:15px">하지만 inline styling은 css보다 성능이 낮고 우선순위가 높아 동적으로 계산하여 스타일링 하는 경우 이외에는 사용을 지양하는 것이 좋습니다.</span></p>
<h3 id="span-stylebackground-color-fffbdc6-self-closing-tagspan"><span style='background-color: #fffbdc'>6) Self-Closing Tag</span></h3>
<p>어떤 태그라도 Self closing tag를 사용할 수 있습니다.
<code>&lt;img&gt;</code>와 같이 하나의 태그가 요소인 경우, <code>&lt;img /&gt;</code>와 같이 항상 <code>/</code>으로 끝내줘야 합니다.
<code>&lt;div /&gt;</code>는 <code>&lt;div&gt;&lt;/div&gt;</code>와 같은 표현입니다.</p>
<h3 id="span-stylebackground-color-fffbdc7-nested-jsxspan"><span style='background-color: #fffbdc'>7) Nested JSX</span></h3>
<p>반드시 최상위를 감싸고 있는 태그가 하나 있어야 합니다.
리액트의 Virtual DOM에서 컴포넌트 변화를 효율적으로 비교할 수 있도록 <strong>한 컴포넌트는 하나의 DOM 트리 구조로 이루어져야 한다</strong>는 규칙이 있기 때문입니다.</p>
<pre><code class="language-javascript">// Bad
const Greetings = () =&gt; {
    return (
        &lt;h1&gt;김개발님!&lt;/h1&gt;
        &lt;h2&gt;위코드에 오신 걸 환영합니다!&lt;/h2&gt;
    );
}

// Good
const Greetings = () =&gt; {
    return (
        &lt;div&gt;
            &lt;h1&gt;김개발님!&lt;/h1&gt;
            &lt;h2&gt;위코드에 오신 걸 환영합니다!&lt;/h2&gt;
        &lt;/div&gt;
    );
}</code></pre>
<h3 id="span-stylebackground-color-fffbdc8-reactfragmentspan"><span style='background-color: #fffbdc'>8) React.Fragment</span></h3>
<p>최상위를 감싸고 있는 태그에 특별한 의미나 스타일이 없다면 불필요한 요소를 생성하게 됩니다.
이때 사용되는 것이 <code>&lt;React.Fragment&gt;</code>로 Fragment는 추가적인 DOM element를 생성하지 않고 하나의 컴포넌트 안에 여러 요소(자식)를 간단하게 그룹화 할 수 있는 기능입니다.</p>
<pre><code class="language-javascript">// &lt;React.Fragment&gt;
const Greetings = () =&gt; {
    return (
        &lt;React.Fragment&gt;
            &lt;h1&gt;김개발님!&lt;/h1&gt;
            &lt;h2&gt;위코드에 오신 걸 환영합니다!&lt;/h2&gt;
        &lt;/React.Fragment&gt;
    );
}

// 축약형 &lt;&gt;...&lt;/&gt;
const Greetings = () =&gt; {
    return (
        &lt;&gt;
            &lt;h1&gt;김개발님!&lt;/h1&gt;
            &lt;h2&gt;위코드에 오신 걸 환영합니다!&lt;/h2&gt;
        &lt;/&gt;
    );
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git & GitHub (2)]]></title>
            <link>https://velog.io/@jin_h2_/Git-GitHub-2</link>
            <guid>https://velog.io/@jin_h2_/Git-GitHub-2</guid>
            <pubDate>Fri, 04 Aug 2023 09:28:32 GMT</pubDate>
            <description><![CDATA[<p><a href="https://velog.io/@jin_h2_/Git-GitHub-1">Git &amp; GitHub (1)</a>에서 git 설치, 많이 사용하는 git 명령어, github로 올리는 방법까지 확인하실 수 있습니다.
아래는 위 링크에 기재된 명령어 외 다른 기본 명령어 입니다.</p>
<h1 id="💻-git-기본-명령어-2">💻 Git 기본 명령어 2</h1>
<pre><code>1. Git clone [repository 주소]
   코드 복제
   레파지토리를 내 로컬로 복제
   (로컬에 아무것도 없는 상태일 때 주로 쓰기 때문에 거의 한 번만 씀)

2. Git branch [브랜치명]
   브랜치를 생성
   브랜치 : 개발할 수 있는 독립된 공간

3. Git checkout [브랜치명]
   현재 브랜치에서 다른 브랜치로 이동

4. Git merge [브랜치명]
   코드 합치기
   로컬에서 현재 브랜치 코드와 특정 브랜치의 코드를 합침

5. Git pull origin [브랜치명]
   코드 가져오기
   특정 브랜치의 코드를 로컬로 가져옴
   * 다른 팀원이 패키지를 받고 push한 것을 pull 하였을 때
react install 진행 해줘야함</code></pre><br>

<h1 id="github-업로드-과정">Github 업로드 과정</h1>
<p>🤓 출처는 wecode.
<img src="https://velog.velcdn.com/images/jin_h2_/post/f586e51c-e925-42f9-8783-44f165f06c82/image.png" alt=""></p>
]]></description>
        </item>
    </channel>
</rss>