<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>seobi-97.log</title>
        <link>https://velog.io/</link>
        <description>즐겁게 코딩 ૮₍ •̀ᴥ•́ ₎ა</description>
        <lastBuildDate>Thu, 25 Jun 2026 23:15:14 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>seobi-97.log</title>
            <url>https://velog.velcdn.com/images/seobi-97/profile/c51cd94f-5ce9-411f-b013-6781ed9c91b3/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. seobi-97.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/seobi-97" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[아티워크(Artiwork) 개발기 - 2. MongoDB를 껐는데 AWS 비용은 왜 폭증했을까? 서버 구조 단순화와 300달러 환불 후기]]></title>
            <link>https://velog.io/@seobi-97/%EC%95%84%ED%8B%B0%EC%9B%8C%ED%81%ACArtiwork-%EA%B0%9C%EB%B0%9C%EA%B8%B0-2.-MongoDB%EB%A5%BC-%EA%BB%90%EB%8A%94%EB%8D%B0-AWS-%EB%B9%84%EC%9A%A9%EC%9D%80-%EC%99%9C-%ED%8F%AD%EC%A6%9D%ED%96%88%EC%9D%84%EA%B9%8C-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%A1%B0-%EB%8B%A8%EC%88%9C%ED%99%94%EC%99%80-300%EB%8B%AC%EB%9F%AC-%ED%99%98%EB%B6%88-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@seobi-97/%EC%95%84%ED%8B%B0%EC%9B%8C%ED%81%ACArtiwork-%EA%B0%9C%EB%B0%9C%EA%B8%B0-2.-MongoDB%EB%A5%BC-%EA%BB%90%EB%8A%94%EB%8D%B0-AWS-%EB%B9%84%EC%9A%A9%EC%9D%80-%EC%99%9C-%ED%8F%AD%EC%A6%9D%ED%96%88%EC%9D%84%EA%B9%8C-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%A1%B0-%EB%8B%A8%EC%88%9C%ED%99%94%EC%99%80-300%EB%8B%AC%EB%9F%AC-%ED%99%98%EB%B6%88-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Thu, 25 Jun 2026 23:15:14 GMT</pubDate>
            <description><![CDATA[<p>두 번째로 AWS 과금 폭탄을 받아 서버 구조를 분석하며 운영, 비용 최적화를 경험했던 이야기를 전달드립니다.</p>
<p>개발을 진행하며, 비용 문제로 mongoDB를 잠시 중단중이였습니다. mock data를 이용하여 개발을 진행했지만 따로 기존에 있는 코드를 손보진 않았습니다. 이때까지는 문제가 될 줄 몰랐죠.</p>
<p>한두달이 지나고.. 카드 결제 내역을 확인하고 AWS 결제 관리 내역을 보게 됐습니다.
<img src="https://velog.velcdn.com/images/seobi-97/post/66f7ea24-381c-4557-af5a-87b8f5312df7/image.png" alt=""></p>
<p>갑자기 Lambda 비용 폭탄이 나왔습니다. 엄청난 과금에 심각성을 느끼고 빨리 원인을 분석하였습니다. </p>
<p>우선 Lambda 함수 로그를 살펴봤습니다.</p>
<blockquote>
<p>&quot;Could not connect to any servers in your MongoDB Atlas cluster - IP that isn&#39;t whitelisted&quot;</p>
</blockquote>
<p>그래.. 데이터베이스 중지를 시켰으니 연결을 못하겠지..라고 생각을 했는데 1월 15일을 기점으로 갑자기 호출 수가 증가한 게 눈에 띄었습니다.
<img src="https://velog.velcdn.com/images/seobi-97/post/f06a438b-2270-454c-9f3e-f3b78d25c29e/image.png" alt=""></p>
<p>문제는 Cold Start를 줄이기 위해 설정해둔 Warmup 호출이었습니다. EventBridge가 1분마다 Lambda를 호출하고 있었는데, 함수가 실행될 때마다 MongoDB Atlas 연결을 시도했습니다.</p>
<p>데이터베이스가 중지된 상태였기 때문에 연결은 실패했고, 결국 타임아웃까지 발생했습니다. 문제는 이러한 실패가 Warmup 스케줄에 의해 계속 반복되면서 불필요한 Lambda 실행 시간이 누적되고 있었다는 점이었습니다.</p>
<p><img src="https://velog.velcdn.com/images/seobi-97/post/122fc0d8-4af0-4721-9b31-7a89cd040d9b/image.png" alt=""></p>
<p>우선 즉각적인 조치로, warmup 호출 주기를 1분에서 5분으로 변경을 했습니다. 이후 warmup의 경우는 mongoDB를 연결하지않고, 실제 API 연동 시에만 mongoDB를 연결하도록 변경했습니다.</p>
<pre><code>// warmup 이벤트에는 DB 연결하지 않음.
if (isWarmup(event)) return &#39;warmup&#39;;
await ensureMongooseConnection({ poolSize: poolsize, readPreference });
</code></pre><p>또한 기존 서버는 Lambda를 VPC 내부에 배치하고, 외부 인터넷 통신을 위해 NAT Gateway와 Elastic IP를 사용하는 구조였습니다.</p>
<p>하지만 현재 서비스는 RDS와 같은 VPC 내부 리소스를 사용하지 않고, 모든 데이터 저장소를 MongoDB Atlas에서 관리하고 있었습니다. 따라서 Lambda가 반드시 VPC 내부에서 실행되어야 할 이유가 없었습니다.</p>
<p>오히려 VPC 구성은 NAT Gateway 비용과 네트워크 복잡성을 증가시키고 있었습니다. 이에 현재 서비스 요구사항을 다시 검토한 결과, Lambda를 VPC 외부에서 실행하는 단순한 구조로 변경해도 기능상 문제가 없다고 판단했습니다.</p>
<p>최종적으로 서버 구조를 다음과 같이 단순화했습니다.</p>
<blockquote>
<p>API Gateway
↓
Lambda
↓
MongoDB Atlas</p>
</blockquote>
<p>오히려 Lambda 비용이 급증함에 따라 다시 서버 전체 리소스를 살펴보는 계기가 되었고 서버 구조를 현재 프로젝트에 더 최적화하여 비용을 최적화 할 수 있었습니다.</p>
<p>AWS 과금 환불 과정은 한 달 정도 걸렸습니다.
저는 S3의 경우는 이미지 파일이 담겨 있어 그것을 제외한 모든 리소스를 삭제했습니다.</p>
<ol>
<li>S3를 제외한 모든 리소스 삭제</li>
<li>공손한 말투로 의도적으로 발생한 게 아닌 스터디, 프로젝트 목적으로 진행하다가 발생을 알림.</li>
<li>각 리소스에 대한 내용, 과금 문서 읽기</li>
<li>비용 모니터링 설정! 과금 발생 시, 알림 설정</li>
</ol>
<p>모든 금액은 아니지만 약 300달러의 금액을 돌려받을 수 있었습니다.
Shout out to Moon-Jeong K!
<img src="https://velog.velcdn.com/images/seobi-97/post/7af816df-878d-4f2a-b1d1-5562543a4589/image.png" alt=""></p>
<h3 id="마치며">마치며</h3>
<p>이번 기회를 통해 모든 리소스들이 어떤 역할을 하고 있었는지 명확하게 알게 되었고, 현재 프로젝트 규모에서 필요한 지 파악할 수 있었습니다. 이를 통해 서버 리소스를 최적화하여 월 고정 인프라 운영 비용 93% 절감할 수 있었습니다.(150달러 -&gt; 약 10달러)
이전에는 기능 구현에 집중했다면, 이번에는 운영 비용과 인프라 구조까지 함께 고려하는 관점으로 프로젝트를 바라볼 수 있었습니다. 개발뿐만 아니라 운영적인 측면에서도 서비스를 고민해본 의미 있는 경험이었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[아티워크(Artiwork) 개발기 - 1. 사용자 경험 개선]]></title>
            <link>https://velog.io/@seobi-97/%EC%95%84%ED%8B%B0%EC%9B%8C%ED%81%ACArtiwork-%EA%B0%9C%EB%B0%9C%EA%B8%B0-1.-%EC%82%AC%EC%9A%A9%EC%9E%90-%EA%B2%BD%ED%97%98-%EA%B0%9C%EC%84%A0</link>
            <guid>https://velog.io/@seobi-97/%EC%95%84%ED%8B%B0%EC%9B%8C%ED%81%ACArtiwork-%EA%B0%9C%EB%B0%9C%EA%B8%B0-1.-%EC%82%AC%EC%9A%A9%EC%9E%90-%EA%B2%BD%ED%97%98-%EA%B0%9C%EC%84%A0</guid>
            <pubDate>Thu, 25 Jun 2026 12:35:51 GMT</pubDate>
            <description><![CDATA[<p>최근에 아티워크가 새 단장을 마치고 리뉴얼 오픈을 했습니다!
아티워크는 작가의 작업 과정을 기록하고, 전시·공모 일정을 한눈에 확인할 수 있는 앱입니다.
현재 App Store, Google Play에 올라가 있으니, 궁금하신 분들은 한번 다운받아 사용해보시면 좋겠습니다.</p>
<p>이번 글에서는 개발하면서 마주쳤던 예기치 못한 문제 3가지와, 이를 어떻게 발견하고 해결했는지를 정리해 보려 합니다.</p>
<h3 id="1-달력-제스처-충돌--상하-스크롤과-좌우-스와이프가-동시에-잡히던-문제">1. 달력 제스처 충돌 — 상하 스크롤과 좌우 스와이프가 동시에 잡히던 문제</h3>
<p>달력 화면에는 두 가지 제스처가 공존합니다. 위아래로 스크롤 하여 달력의 확장 영역을 탐색하고, 좌우로 스와이프 시, 월 전환을 합니다. 그런데 손으로 하는 제스처다 보니 정확히 수직, 수평으로만 움직이지 않습니다. 두 방향이 동시에 인식되면서, 의도와 달리 달력이 대각선으로 끌려가는 현상이 생겼습니다.</p>
<p>원인은 세로 스크롤과 좌우 월 전환 제스처가 경쟁 상태(race condition)에 놓인 것이었습니다. 그래서 제스처에 우선순위를 부여하는 로직을 직접 구현했습니다.(<a href="https://www.npmjs.com/package/react-native-gesture-handler">react-native-gesture-handler</a>)</p>
<pre><code>if (type === &#39;month&#39;) {
      panGesture = panGesture.activeOffsetX([-6, 6]).failOffsetY([-24, 24]);
}</code></pre><ul>
<li>좌우로 6px 이상 움직이면 Pan 제스처가 활성화됩니다.</li>
<li>Pan이 활성화되기 전에 상하로 24px 이상 움직이면 Pan이 실패(fail) 하고, 터치는 ScrollView의 세로 스크롤에 넘깁니다.</li>
</ul>
<p>즉, 특정 방향 이동량이 임계값을 넘으면 해당 방향 스크롤에 우선권 부여하여 사용자가 원하는 방향으로 이동하게끔 수정했습니다. </p>
<h3 id="2-작업-사진-저장-시-사용자-체감-시간-증가---비동기-업로드로-풀어내기">2. 작업 사진 저장 시, 사용자 체감 시간 증가 - 비동기 업로드로 풀어내기</h3>
<p>작업 사진을 등록하는 과정은 아래와 같이 긴 과정을 거칩니다. </p>
<blockquote>
<p><strong><em>사진 촬영 → 캡션 입력 → signed URL 발급 → S3 업로드 → picture/caption API 호출 → 홈 탭으로 이동</em></strong></p>
</blockquote>
<p>업로드 파이프라인이 길고 순차적으로 실행되다 보니 사용자가 작업 사진 등록을 기다리는 시간이 길었습니다.
홈 화면에서는 이미 스켈레톤 UI가이 적용돼 있다는 점에 착안했습니다. 사용자를 먼저 홈으로 이동시키고, 업로드는 백그라운드에서 처리하는 방식입니다.</p>
<ul>
<li>imageUri, task, date, caption을 payload로 먼저 고정한 뒤, void (async () =&gt; { ... })()로 화면과 무관하게 업로드를 실행</li>
<li>서로 의존하지 않는 로컬 이미지 읽기와 signed URL 발급은 Promise.all로 동시에 실행</li>
</ul>
<p>다만 한 가지를 고려하지 않았습니다. 사용자가 빠르게 홈 화면으로 이동을 했지만, 제대로 업로드가 완료됐는지 모릅니다. 그래서 전역 업로드 토스트를 만들어 진행 중·실패 상태를 앱 하단에 항상 노출했습니다.</p>
<p>결과적으로 즉시 화면을 복귀시켜 체감 대기 시간을 줄이고, 진행 상황을 한눈에 보여주면서 중복 제출까지 막을 수 있었습니다.
<img src="https://velog.velcdn.com/images/seobi-97/post/60a99804-dcb8-4f4b-8648-1abccf2c5b13/image.png" alt=""></p>
<h3 id="3-미술달력-탭-이동시-데이터-지연---prefetch로--성능-개선">3. 미술달력 탭 이동시 데이터 지연 - prefetch로  성능 개선</h3>
<p>미술 달력 탭에 진입하면 전시 공모 프로그램, 찜 목록, 광고 등 다양한 API를 호출합니다. 그래서 달력 프레임은 먼저 떠도, 그 안을 채우는 데이터가 한 박자 늦게 노출되는 현상이 있었습니다.</p>
<p>이를 React Query의 prefetch로 해결했습니다. 로그인.온보딩 완료 직후 usePrefetchProgramCalendar로 React Query 캐시에 선적재하고, 탭 버튼을 누르는 시점에도 한 번 더 호출해 최신 데이터를 보장했습니다.</p>
<p>이 조건을 통해서 “메인 탭(작업 달력 홈)에 정상 진입한 사용자”일 때만 백그라운드 prefetch를 돌렸습니다.</p>
<pre><code>// _layout.tsx

// 로그인을 한, 작업 데이터를 불러오는 중이 아닌, 작업이 하나 이상 있을 때
// OAuth 가입자가 첫 작업 만든 뒤 약관 동의 화면에 있을 때 prefetch 동작
const canPrefetchProgram = !!session &amp;&amp; !isTasksLoading &amp;&amp; !!tasks &amp;&amp; tasks.length &gt; 0 &amp;&amp; !needsOAuthPostTaskTerms;
  usePrefetchProgramCalendar(canPrefetchProgram);</code></pre><pre><code>// prefetchProgramCalendar.ts

// 미술달력 탭 진입 전 contests·ads(detail)·pin·programs 데이터를 React Query 캐시에 적재
export function prefetchProgramCalendarQueries(...) {
  return Promise.all([
    queryClient.prefetchQuery({ ... &#39;/contests&#39; ... }),
    queryClient.prefetchQuery({ ... &#39;/ads&#39; placement: &#39;detail&#39; ... }),
    queryClient.prefetchQuery({ ... &#39;/contests/pin&#39; ... }),
    queryClient.prefetchQuery({ ... &#39;/programs&#39; ... }),
    queryClient.prefetchQuery({ ... &#39;/programs/pin&#39; ... }),
  ]);
}</code></pre><p><img src="https://velog.velcdn.com/images/seobi-97/post/7e53b374-19ca-4fb4-8420-5ce8e2c8d1b3/image.png" alt=""></p>
<h3 id="마치며">마치며</h3>
<p>3가지 모두 개발을 하며 <strong>&quot;동작은 하지만, 사용자 입장에서 생각했을때 이런 부분은 불편하지 않을까?&quot;</strong> 생각하며 개선할 점을 찾았습니다.</p>
<p>수직, 수평 제스처 동작을 점검하면서 웹과는 다른 모바일만의 인터랙션을 경험할 수 있었고, 업로드의 백그라운드 처리와 데이터 처리 타이밍 문제까지 차근차근 들여다볼 수 있었던 좋은 시간이었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[✨SIPE Meetup 내친소✨]]></title>
            <link>https://velog.io/@seobi-97/SIPE-Meetup-%EB%82%B4%EC%B9%9C%EC%86%8C</link>
            <guid>https://velog.io/@seobi-97/SIPE-Meetup-%EB%82%B4%EC%B9%9C%EC%86%8C</guid>
            <pubDate>Sat, 06 Jun 2026 06:28:27 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/seobi-97/post/5334aba8-636f-41e3-ac65-59de79b14e0f/image.jpg" alt=""></p>
<h2 id="sipe-meetup-내친소">SIPE Meetup 내친소</h2>
<p>이번에도 친구 찬스로 IT 네트워킹 자리에 다녀왔습니다!
정말 다양한 세션이 준비되어 있어서 많이 기대를 했는데 역시 정말 즐거운 시간이였습니다. 개발자 네트워킹 모임은 여러 번 가봤지만, 이번 모임은 특히 더 즐겁게 대화에 집중할 수 있는 분위기였습니다.
가끔 네트워킹 모임을 나가면 이야기를 나눌 수 있는 시간이 짧아 아쉬운 경험이 있었는데 아래 타임 테이블과 같이 네트워킹 시간이 아주 많았어요.
<img src="https://velog.velcdn.com/images/seobi-97/post/f4c7c493-46db-4db7-a0c9-86c6563e7c44/image.png" alt=""></p>
<h2 id="네트워킹">네트워킹</h2>
<p>각 네트워킹 시간마다 조를 편성해서 여러 사람들과 소통할 수 있도록 해주셨어요. 각 시간마다 미션 보드와 같이 이야기를 나눌 수 있는 주제도 주셔서 어떤 이야기를 나누지?라는 고민이 전혀 없었습니다.</p>
<p>첫 모임에서는 간단히 자기소개 후, 각자의 취미를 소개하며 가까워지는 시간을 가졌어요. 저는 마라톤, 클라이밍 등 운동위주로 활동을 하는데 음악을 즐기시는 분들이 꽤 많았어요! 그리고 각자 다니고 있는 회사에서는 어떤 툴을 사용하는지, 어느 역할까지 맡아서 하는지 이야기를 나누었어요. 요즘 확실히 회사에서는 Claude를 많이 사용하는거 같은데 개인적으로는 Codex를 많이들 쓴다 하더라고요. 그리고 자신의 회사 채용 현황에 대한 약간의 어필 시간..! </p>
<p>두번째 모임에서는 좀 더 커리어와 AI에 대한 시각으로 깊게 이야기를 나누었어요. 요즘 다들 AI를 직접 사용을 하며, 발전에 대한 체감을 하게 되고 그거에 대해 개발자의 역할이 어떻게 변할지, 어떤 방식으로 나아가야 되는지 고민이 많더라고요! 
요즘은 AI 덕분에 코드 작성 자체의 난이도는 점점 낮아지고 있다고 느껴요. 대신 문제를 정의하고, 서비스의 흐름을 이해하고, AI가 낸 결과를 검증하는 능력이 더 중요해지는 것 같습니다.
예전에는 프론트엔드, 백엔드, 인프라처럼 역할이 명확하게 나뉘었다면 앞으로는 한 사람이 더 넓은 영역을 이해해야 하는 시대가 오지 않을까 하는 생각도 들었습니다. 정작 AI를 누구보다 많이 쓰고 있지만 딱 떠오르는 AI 서비스가 없는게 신기했어요.</p>
<p>게임 세션에서 특히 인상 깊었던 건 &#39;타이퍼즈&#39;라는 게임이었습니다. 단순한 타자 게임이 아니라 실제 코드 타이핑을 기반으로 점수를 계산하는 게임이었는데, &quot;개발자라면 누구나 공감할 만한 요소를 이렇게 게임으로 만들 수 있구나&quot; 싶었어요.</p>
<p>무엇보다 AI를 활용해 비교적 짧은 시간 안에 구현했다는 이야기를 듣고, 이제는 아이디어를 실제 서비스로 만드는 장벽이 정말 많이 낮아졌다는 것을 다시 한번 체감했습니다. 또 개발 언어, 난이도를 선택해서 타자 속도와 정확도에 따라 점수를 내는데 어떤 비중으로 어떻게 로직을 짜셨는지 궁금하더라고요!
<img src="https://velog.velcdn.com/images/seobi-97/post/cd70749a-eb7b-4cb6-894d-413a08a30d3d/image.png" alt=""></p>
<h2 id="마무리">마무리</h2>
<p>최근에는 직장 생활을 하면서 개발이 점점 일로만 느껴질 때가 많이 있었습니다. 새로운 기술을 찾아보거나 개인 프로젝트를 만드는 일도 예전만큼 설레지 않았고요.</p>
<p>그런데 최근 네트워킹 모임을 다니며 다양한 분야의 개발자들이 각자의 고민과 경험을 이야기하는 모습을 보며 좋은 자극을 받았습니다. 누군가는 아이디어를 내어 사이드 프로젝트를 만들고 있었고, 누군가는 AI를 활용해 새로운 기술을 시도하고 있었어요.</p>
<p>저도 최근에 앱을 개발해 앱스토어에 배포하는 경험을 했습니다. 처음에는 기능을 구현하고 출시하는 것 자체에 집중했다면, 이제는 조금 다른 고민을 하게 되는 것 같습니다.</p>
<p>&#39;어떻게 만들까?&#39;보다 &#39;왜 만들까?&#39;를 먼저 고민하고, 개발자의 시선이 아닌 사용자의 시선에서 서비스를 바라보는 것 말입니다. 사용자는 어떤 순간에 불편함을 느끼는지, 어떤 경험이 서비스를 다시 찾게 만드는지, 그리고 작은 기능 하나가 실제로 어떤 가치를 줄 수 있는지 더 깊게 생각해보고 싶어졌습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[✨AWSKRUG 프론트엔드 소모임 ✨]]></title>
            <link>https://velog.io/@seobi-97/AWSKRUG-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%86%8C%EB%AA%A8%EC%9E%84</link>
            <guid>https://velog.io/@seobi-97/AWSKRUG-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%86%8C%EB%AA%A8%EC%9E%84</guid>
            <pubDate>Wed, 29 Apr 2026 15:29:26 GMT</pubDate>
            <description><![CDATA[<h2 id="참여-계기">참여 계기</h2>
<p>2026년 4월 29일 수요일 *<em>AWSKRUG 프론트엔드 소모임 *</em>에 참여했다. 작년에 대학동기가 추천해서 한번 들었었다. 세션은 장비 이슈로 조금 아쉬웠지만 다양한 사람들과 이야기를 나누고 동기부여도 얻고 좋은 경험이였다. 그래서 이번에 메일로 소모임 알림이 뜨길래 다시 참여하게 되었다.</p>
<h2 id="ai에-대한-다양한-생각">AI에 대한 다양한 생각</h2>
<p>요즘에 개발자 친구들만 만나면 하는 이야기가 AI이다. 처음 대학에 입학했을때와 너무 달라진 개발 세상에 다들 적응하려고 노력해가고 있다. 다들 불안감은 조금 있지만 개발자의 역할에 대해 다시 정의하려고 하는거 같다. 요즘에 AI에 대한 역할도 너무 빠르게 변하고 있는거 같다. 이전에는 검색을 대체하는 정도의 정보 바탕으로만 사용하다가, 코드의 오류를 찾고 해결하는 정도에서 이제 프로젝트의 시작과 끝 모두 맡길 수 있을만큼 변화했다. 그러면서 지금 새로 업데이트 되는 AI 툴에 대한 관심이 쏠리고 있다. 현재는 확실히 클로드가 대세이긴 하지만 각자에게 맞는 툴을 사용해서 써도 될만큼 상향 평준화가 됐다.</p>
<h2 id="라이트닝-발표">라이트닝 발표</h2>
<p>첫번째 발표자 분은 통제가 없으면 시스템 자체가 흔들린다고 봤다. 사원이 코드를 짜거나 20년차 팀장이 코드를 짜거나 같거나 거의 비슷하게 코드가 작성되어야한다고 봤다. 그러기 위해서 통제를 철저하게 해야한다고 이야기하셨다. 또한 요즘 md파일로 AI에게 규칙을 부여하는데 이게 너무 많아지게 되면 AI 스스로 권장사항으로 받아드릴 수 있게 때문에 인간의 뇌처럼 7단계로 분류하여 관리를 하고 있다고 하셨다.
두번째 발표자 분은 피드백 루프를 빠르게 돌리는 방향이 나은거 같다고 이야기 하셨다. 엄청난 토큰을 사용하신 수집 결과도 보여주셨다.</p>
<p>첫번째 분과 두번째 분의 AI에 대한 관점 차이점이 존재하여 다들 흥미롭게 들었던 거 같다. 그래서 발표 분위기도 좋았고, 질문도 상당히 많이 나왔다.</p>
<p>이 모임에 나가기전에 <a href="https://yozm.wishket.com/magazine/detail/3703/">요즘IT 글</a>을 하나 봤는데 딱 그 얘기가 나와서 신기했다.
간단히 요약하면 Anthropic 코드가 유출되면서 사람들이 평을 남겼는데, 형편없는 코드다. 6만줄이 넘는 코드에도 오류 검증이 없었다.라는 비판을 했는데 아이러니하게도 요즘 최고의 호응을 얻고있는 아이러니한 현실이다.</p>
<blockquote>
<p><strong>실수를 막는 게 회복하는 것보다 낫다 vs 실수는 어차피 생긴다, 그러면 빨리 알고나서 고치는 게 낫다</strong></p>
</blockquote>
<p>요즘 사람들이 고민하는 부분인 거 같다. 현재 내가 하고있는 코드 방식은 전자에 가깝다. 그래서 현재 토큰이 남는게 아깝다고 생각하고 있었는데 이번 기회에 여러 방식으로 AI를 다뤄보며 토큰을 야무지게 써야겠다는 생각이 들었다.</p>
<p>또 마지막에 질문 시간을 갖다보니 토큰의 비용이 더 비싸질까? 싸질까?에 대한 의견들을 나누었는데 개인적인 생각으로는 더 비싸지지 않을까라는 생각을 한다. 현재 AI에 대해 엄청난 에너지에 대해 이야기가 많은데 지금보다도 더 AI가 발전하고 실생활에 적용이 된다면 엄청난 에너지를 사용할거라 생각한다.</p>
<p>이번에 다양한 사람들과 AI 도구를 어떻게 사용하는게 효과적일지, 개인, 조직에 따라 어떤 관점으로 다뤄야하는지 이야기를 나눠보고 재밌었다. 이런곳에 오면 훌륭하신 분들도 많아서 많이 배우고 가는 느낌이 든다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[✨SIPE 네 번째 사담콘 참가 후기!✨]]></title>
            <link>https://velog.io/@seobi-97/SIPE-%EB%84%A4-%EB%B2%88%EC%A7%B8-%EC%82%AC%EB%8B%B4%EC%BD%98-%EC%B0%B8%EA%B0%80-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@seobi-97/SIPE-%EB%84%A4-%EB%B2%88%EC%A7%B8-%EC%82%AC%EB%8B%B4%EC%BD%98-%EC%B0%B8%EA%B0%80-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Thu, 28 Aug 2025 10:06:53 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/seobi-97/post/a3430e36-3ce5-4a2e-a2ba-9849d8b755e5/image.png" alt=""></p>
<h2 id="sipe-네-번째-사담콘-참석-후기">SIPE 네 번째 사담콘 참석 후기</h2>
<p>얼마 전, <em>SIPE(SHARING INSIGHTS with PEOPLE EVERYDAY)</em>에서 진행한 사담콘에 다녀왔습니다.
너무 재밌게 보고 정리도 할 겸, 참가한 제 경험을 공유해보려고합니다!</p>
<h2 id="1-sipe란">1. SIPE란?</h2>
<p>SIPE는 Sharing Insights with People Everyday의 약자로, 단순히 프로젝트 완성에만 초점에 둔 게 아니라, 다양한 교류 활동을 통해 성장하는데 초점인 IT 동아리입니다.
주요 활동으로는 크게 세 가지가 있다해요!</p>
<ul>
<li>정규 미션
한 기수에 2번의 정규 미션을 진행한다. 자유롭게 팀을 구성하여 원하는 개발, 공부에 대한 미션을 수행한다.</li>
<li>사이프챗
개발, 커리어, 이직, 주식, 부업 등 다양한 대화 주제를 가지고 소통하는 시간이다. 다른 사람의 경험을 재미나게 들을 수 있는 시간인 거 같아요.</li>
<li>사담콘
현직 개발자, 디자이너, PM의 실제 업무에서 많이 고민을 했던 기술, 커리어에 대한 이야기를 나누는 컨퍼런스이다. 해당 사담콘은 사이퍼뿐 아니라 외부 인원들도 신청을 통해 참가할 수 있다.</li>
</ul>
<blockquote>
<p>사이프 공식 사이트를 활용하여 적었습니다.
자세한 소개와 활동은 <a href="https://sipe.team/">https://sipe.team/</a> 에서 확인할 수 있습니다!</p>
</blockquote>
<h2 id="2-사담콘-참가">2. 사담콘 참가</h2>
<p>이번 사담콘은 공덕역 프론트원에서 열렸습니다.
부트캠프에서 알게 되어 꾸준히 연락하는 친구가 “우리 동아리에서 컨퍼런스를 열어! 부담없이 한번 들어봐!”라고 권유해줘서 참석하게 됐습니다.
미리 준비한 QR코드로 빠르게 입장했고, 홀에는 간단한 과자와 음료가 준비되어 있었습니다. 중간에 당 떨어질 때 방문하기 딱 좋은..
무겁고 진지한 분위기로 생각했지만, 생각보다 훨씬 활기찼고 참여자도 많았어요!
포스트잇에 인스타 아이디나 번호를 적어 랜덤으로 교환해서 ‘커피챗’ 상대를 찾는 이벤트도 있었어요. 저는 망설이다 기회를 놓쳤습니다..!
<img src="https://velog.velcdn.com/images/seobi-97/post/982a5629-e9d0-456b-b58e-2634bd7ad600/image.jpeg" width="50%"/></p>
<h3 id="발표-세션">발표 세션</h3>
<p><img src="https://velog.velcdn.com/images/seobi-97/post/c9710c83-ae2a-435a-8fdb-1fae2cf9ee08/image.png" alt=""></p>
<p>발표는 백엔드, AI, PM, 개발 유튜버 등 다양한 분야에서 종사하는 분들이 준비하셨어요.
처음에는 “프론트엔드 얘기 아니면 잘 이해 못하고 흥미도 떨어지지 않을까” 걱정했는데, 발표자분들이 다양한 사람이 모일 걸 예상하고 눈높이에 맞는 설명과 비유를 곁들여서 고민을 공감하며 재밌게 들을 수 있었습니다.</p>
<p>특히 인상 깊었던 내용들은</p>
<ul>
<li>경품 이벤트 분산락 문제 해결 시도</li>
<li>AI의 환각(hallucination)과 안정성/비용/응답속도 이슈</li>
<li>개발 유튜버의 성장 과정</li>
<li>패키지를 잘 분리하려다보니 배보다 배꼽이 더 커진 경험담!</li>
</ul>
<p>저는 첫 세션부터 노트북을 펴고 메모할 만큼 몰입해서 들었습니다.
<img src="https://velog.velcdn.com/images/seobi-97/post/d535fcbd-abeb-40d3-bbf7-7578a8b85927/image.jpeg" width="50%"/></p>
<h3 id="네트워킹-시간">네트워킹 시간</h3>
<p>대학생 때 이후로 오랜만에 발표를 오래 듣다보니 예상치 못한 체력 이슈가 있었습니다. 살짝 정신이 혼미해질때쯤 쉬는 시간이 주어졌어요
홀에서 친구와 함께 돌아다니며 많은 개발자 분들과 인사를 나눌 수 있었습니다. “같은 분야네요!”, “저는 이런 분야 관심있어요!” 하면서 대화도 하고, 처음 본 분들과 인생네컷도 찍는 진귀한(?) 경험도 했습니다!
<img src="https://velog.velcdn.com/images/seobi-97/post/d5304e6f-dd2a-4b18-a217-23f4aa9a5afe/image.jpg" width="50%"/>
그리고 정말 놀랐던 건, 제 대학 한 학번 뒤 동기가 동아리 1기부터 활동하고 있었다는 겁니다.
한번 마주치기도 힘든데 최근에 세 번이나 우연히 마주치다니..꼭 밥 한 끼 해야겠네요.</p>
<h2 id="마무리">마무리</h2>
<p align=center><img src="https://velog.velcdn.com/images/seobi-97/post/03cfa5fe-c5fd-450c-9e25-457a02126cbd/image.jpeg" width="50%"/></p>

<p>행사 마지막에는 경품 추첨이 있었습니다. 개발 서적, 사이프 굿즈가 걸려있었는데 역시 똥손인 저는 바로 광탈! 친구는 책에 당첨됐습니다.</p>
<h2 id="느낀-점">느낀 점</h2>
<p>저는 커뮤니티 활동 경험도 많지 않고, 제 실력이 뛰어나다고 생각하지 않아서 사실 걱정이 많았습니다. “과연 내가 대화를 잘할 수 있을까? 다른 사람 얘기를 잘 이해할 수 있을까..?</p>
<p>하지만 괜한 걱정을 한 거 같아요! 발표는 이해하기 쉽게 준비되어 있었고, 참가자분들도 먼저 인사해주시고 편하게 이야기를 나눌 수 있었어요.
이번 경험 덕분에 앞으로는 더 많은 사람들과 개발, 커리어, 취미 등 다양한 주제로 얘기를 나누고 싶다는 생각을 했습니다.</p>
<p>벌써 다음 사담콘이 기대됩니다!🙌</p>
<blockquote>
<p>추후에 아래 유튜브 계정으로 사담콘 영상이 업로드 된다고 합니다!
<a href="https://www.youtube.com/@sipe_team/videos">https://www.youtube.com/@sipe_team/videos</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[A text node cannot be a child of a <View>.]]></title>
            <link>https://velog.io/@seobi-97/A-text-node-cannot-be-a-child-of-a-View</link>
            <guid>https://velog.io/@seobi-97/A-text-node-cannot-be-a-child-of-a-View</guid>
            <pubDate>Mon, 10 Mar 2025 06:29:20 GMT</pubDate>
            <description><![CDATA[<p>React Native를 이용해 운동 예약 앱을 만들던 도중 <strong>A text node cannot be a child of a View.</strong> 에러를 발견했다.
 대충 에러 내용을 보면 View의 자식 노드로 있는 text에 문제가 있는것으로 보인다.</p>
<p> 회원가입 페이지인데, 글자 수가 2글자 이하거나 정규식이 맞지 않을때 경고 메세지를 보여주려했다.</p>
<blockquote>
<pre><code>&lt;View style={[styles.container, colorScheme === &#39;dark&#39; &amp;&amp; styles.darkContainer]}&gt;
            &lt;Text style={[styles.title, colorScheme === &#39;dark&#39; &amp;&amp; styles.darkText]}&gt;회원가입&lt;/Text&gt;
            &lt;TextInput
                style={[styles.input, colorScheme === &#39;dark&#39; &amp;&amp; styles.darkInput]}
                placeholder=&quot;이름&quot;
                value={formData.name}
                onChangeText={text =&gt; handleChange(&#39;name&#39;, text)}
            /&gt;
            &lt;TextInput
                style={[styles.input, colorScheme === &#39;dark&#39; &amp;&amp; styles.darkInput]}
                placeholder=&quot;이메일&quot;
                value={formData.email}
                onChangeText={text =&gt; handleChange(&#39;email&#39;, text)}
            /&gt;
            &lt;TextInput
                style={[styles.input, colorScheme === &#39;dark&#39; &amp;&amp; styles.darkInput]}
                placeholder=&quot;비밀번호&quot;
                value={formData.password}
                onChangeText={text =&gt; handleChange(&#39;password&#39;, text)}
                secureTextEntry
            /&gt;
            {errors.name &amp;&amp; &lt;Text style={styles.errorText}&gt;{errors.name}&lt;/Text&gt;}
            {errors.email &amp;&amp; &lt;Text style={styles.errorText}&gt;{errors.email}&lt;/Text&gt;}
            {errors.password &amp;&amp; &lt;Text style={styles.errorText}&gt;{errors.password}&lt;/Text&gt;}
            &lt;Button title=&quot;회원가입&quot; onPress={handleSignup} /&gt;
        &lt;/View&gt;</code></pre></blockquote>
<pre><code>
하지만 해당 에러 구문에서 에러가 발생하였다.
원래 코드의 문제점은 errors.name이 빈 문자열일 경우 false를 반환하지만, JavaScript에서는 false도 하나의 값으로 인식하여 렌더링을 시도한다는 점이다. 따라서 View에서는 false값을 직접적으로 가질 수 없기 때문에 에러가 발생하는것이다.

따라서 아래와 같은 코드로 수정하였다.</code></pre><p>{errors.name ? <Text style={styles.errorText}>{errors.name}</Text> : null}
{errors.email ? <Text style={styles.errorText}>{errors.email}</Text> : null}
{errors.password ? <Text style={styles.errorText}>{errors.password}</Text> : null}</p>
<p>```</p>
<p>null값일 경우 렌더링을 하지 않기 때문에, 에러가 발생하지 않는다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring Boot + Kotlin] MariaDB 설치 및 연동하기]]></title>
            <link>https://velog.io/@seobi-97/Spring-Boot-Kotlin-MariaDB-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@seobi-97/Spring-Boot-Kotlin-MariaDB-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 28 Jun 2024 08:36:52 GMT</pubDate>
            <description><![CDATA[<p>리눅스 서버에 MariaDB를 설치하고, 설치된 MariaDB을 연동하는 방법에 대해 알아보겠다.</p>
<h2 id="mariadb-설치">MariaDB 설치</h2>
<pre><code>// 설치
sudo apt-get update
sudo apt-get install mariadb-server</code></pre><p>설치 과정중에 비밀번호 설정이 뜰 것이다.</p>
<pre><code>Enter current password for root(enter for none):</code></pre><pre><code>// 서비스 시작 및 확인
sudo systemctl start mariadb
sudo systemctl enable mariadb
sudo systemctl status mariadb</code></pre><p><code>sudo systemctl status mariadb</code>를 확인했을때 별 다른 에러메시지가 없다면 정상적으로 실행된 것이다.</p>
<p>아래 여러 항목에 대해서 묻는데 원하는 취향에 따라 설정해주면 된다.</p>
<pre><code>// 패스워드 입력
// 초기에 설정한 패스워드를 입력해주었다.
New password:
Re-enter new password:

// anonymous 계정을 삭제하겠습니까?
Remove anonymous users?

// 원격으로 root 로그인을 허용하지 않습니까?
Disallow root login remotely?

// test database를 삭제하겠습니까?
Remove test database and access to it?

// 지금 권한 테이블을 다시 로드하시겠습니까?
Reload privilege tables now?</code></pre><p>아래 명령어로 mariaDB에 접속이 가능하다.</p>
<pre><code>sudo mysql -u root -p</code></pre><h2 id="mariadb-연동하기">MariaDB 연동하기</h2>
<ol>
<li>먼저 <code>build.gradle.kts</code> 파일을 설정해준다.<pre><code>plugins {
 id(&quot;org.springframework.boot&quot;) version &quot;3.3.1&quot;
 id(&quot;io.spring.dependency-management&quot;) version &quot;1.1.5&quot;
 kotlin(&quot;plugin.jpa&quot;) version &quot;1.9.24&quot;
 kotlin(&quot;jvm&quot;) version &quot;1.9.24&quot;
 kotlin(&quot;plugin.spring&quot;) version &quot;1.9.24&quot;
}
</code></pre></li>
</ol>
<p>group = &quot;com.example&quot;
version = &quot;0.0.1-SNAPSHOT&quot;</p>
<p>java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}</p>
<p>repositories {
    mavenCentral()
}</p>
<p>dependencies {
    implementation(&quot;org.springframework.boot:spring-boot-starter-data-jpa&quot;)
    implementation(&quot;org.springframework.boot:spring-boot-starter-mustache&quot;)
    implementation(&quot;org.springframework.boot:spring-boot-starter-web&quot;)
    implementation(&quot;com.fasterxml.jackson.module:jackson-module-kotlin&quot;)
    implementation(&quot;org.jetbrains.kotlin:kotlin-reflect&quot;)
    implementation(&quot;io.github.cdimascio:dotenv-kotlin:6.2.2&quot;)
    implementation(&quot;org.mariadb.jdbc:mariadb-java-client&quot;)
    developmentOnly(&quot;org.springframework.boot:spring-boot-devtools&quot;)
    testImplementation(&quot;org.springframework.boot:spring-boot-starter-test&quot;)
    testImplementation(&quot;org.jetbrains.kotlin:kotlin-test-junit5&quot;)
    runtimeOnly(&quot;org.mariadb.jdbc:mariadb-java-client&quot;)
    testRuntimeOnly(&quot;org.junit.platform:junit-platform-launcher&quot;)
}</p>
<p>kotlin {
    compilerOptions {
        freeCompilerArgs.addAll(&quot;-Xjsr305=strict&quot;)
    }
}</p>
<p>tasks.withType<Test> {
    useJUnitPlatform()
}</p>
<pre><code>2. 데이터베이스 설정
`application.yml`이나 `application.properties`를 작성한다.
</code></pre><p>// application.properties</p>
<p>spring.application.name=kotlin_crud
spring.datasource.url=jdbc:mariadb://localhost/crud
spring.datasource.username=${DB_USERNAME}
spring.datasource.password=${DB_PASSWORD}
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver</p>
<p>spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDB106Dialect</p>
<p>```
<code>ddl-auto</code>는 DDL(Data Definition Language)을 자동으로 작성하여 테이블을 생성하거나 수정해주는 속성이다. </p>
<p><code>show-sql</code>은 콘솔로그로 sql문을 확인할 수 있다.</p>
<p><code>format_sql</code>은 sql문을 포맷팅해서 보기 편하게 만들어준다.</p>
<p>서버의 데이터베이스를 사용한다면, <code>localhost</code>에 서버 주소를 입력하면 된다.
<code>localhost</code>뒤에는 사용할 데이터베이스 이름을 넣어주면 된다.
서버 주소, 데이터베이스 유저 이름, 패스워드는 노출될 수도 있기에 환경 변수로 사용하였다.</p>
<p>이제 애플리케이션을 실행하면 MariaDB와 연동된 Spring Boot를 사용할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring Boot + Kotlin] Kotlin project 생성하기 ]]></title>
            <link>https://velog.io/@seobi-97/Spring-Boot-Kotlin-project-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@seobi-97/Spring-Boot-Kotlin-project-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 26 Jun 2024 07:42:15 GMT</pubDate>
            <description><![CDATA[<h2 id="1-intellij-idea-설치">1. IntelliJ IDEA 설치</h2>
<p>IntelliJ IDEA를 이용하여 프로젝트를 설정한다.
아래 사이트에서 다운로드 가능하고, 계정이 따로 없을 경우 30일 체험판 또는 무료 버전을 다운받고, 학생 계정이 있으면 확인하여 등록을 해주면 된다.
(<a href="https://www.jetbrains.com/ko-kr/idea/">https://www.jetbrains.com/ko-kr/idea/</a>)
<img src="https://velog.velcdn.com/images/seobi-97/post/d979b189-d872-41c9-9659-f414479c4658/image.png" alt=""></p>
<h2 id="2-jdk-설치">2. JDK 설치</h2>
<p><img src="https://velog.velcdn.com/images/seobi-97/post/142c1143-8f9b-4ac3-a957-6db2ba85a312/image.png" alt=""></p>
<p>리눅스 서버에 설치된 버전과 맞추기 위해 jdk-17 버전을 다운받았다.
개발환경에 맞는 버전을 선택해서 다운받으면 된다.
(<a href="https://www.oracle.com/kr/java/technologies/downloads/#java17">https://www.oracle.com/kr/java/technologies/downloads/#java17</a>)</p>
<h2 id="3-프로젝트-생성하기">3. 프로젝트 생성하기</h2>
<p><img src="https://velog.velcdn.com/images/seobi-97/post/550445bd-7b38-4fa5-9c04-0e92766e5919/image.png" alt="">
처음 안내 페이지에서 생성해도 되고, 상단 메뉴바에 File-&gt;New-&gt;Project를 선택해도 된다.
JDK는 아까 설치했던 17버전을 선택해줬다.</p>
<h2 id="4-dependecies-선택하기">4. Dependecies 선택하기</h2>
<p><img src="https://velog.velcdn.com/images/seobi-97/post/6cf86d56-17ca-4de5-a2a5-a5e4fd8720ba/image.png" alt=""></p>
<p><a href="https://spring.io/guides/tutorials/spring-boot-kotlin">공식문서</a>에 따르면, 5개의 dependencies를 선택하라고 나와있다.
나는 mariaDB를 사용할 예정이기에, H2 Database 대신 MariaDB Driver를 선택해줬다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[PostgreSQL vs MySQL]]></title>
            <link>https://velog.io/@seobi-97/PostgreSQL-vs-MySQL</link>
            <guid>https://velog.io/@seobi-97/PostgreSQL-vs-MySQL</guid>
            <pubDate>Wed, 26 Jun 2024 02:15:43 GMT</pubDate>
            <description><![CDATA[<h2 id="개요">개요</h2>
<p>대학교 수업 시간에 Database, MySQL에 대해 다뤄봤지만, 기억이 나지 않을뿐더러 너무 오래된 내용이었다. 지금까지 프론트엔드에 대해서만 공부를 하고 백엔드에 대해 지식이 많이 부족한 상태이다. 백엔드 공부의 필요성을 느끼고 차근차근 공부중이다.</p>
<p>Oracle, MySQL, Redis, MongoDB 등 다양한 Database에 들어봤지만, 그 중 PostgreSQL, MySQL에 대해 다뤄볼 예정이다.</p>
<h2 id="postgresql이란">PostgreSQL이란?</h2>
<blockquote>
<p>객체 관계형 데이터 베이스 관리 시스템(ORDBMS)이다.</p>
</blockquote>
<p>PostgreSQL은 JSON, XML등 다양한 데이터 타입을 지원하며, 고급 인덱싱 기능, 복잡한 쿼리 최적화, MVCC 등을 제공한다. 
따라서 복잡한 대용량 데이터 작업에 강점이 있다.
데이터 유형, 확장성, 동시성 및 데이터 무결성에 있어 유연성이 더 뛰어나다.</p>
<h3 id="mvccmulti-version-concurrency-control">MVCC(Multi-Version Concurrency Control)</h3>
<p>데이터베이스 관리 시스템에서 동시성 제어를 위해 사용하는 기법 중 하나이다.
MVCC는 데이터베이스의 각 레코드에 여러 버전을 저장한다. 각 트랜잭션은 레코드의 특정 버전을 참조한다. 이로 인해 읽기 작업이 쓰기 작업을 방해하지 않으며, 쓰기 작업도 다른 트랜잭션의 읽기작업을 방해하지 않는다. </p>
<h2 id="mysql이란">MySQL이란?</h2>
<blockquote>
<p>오픈 소스 관계형 데이터베이스 관리 시스템(RDBMS)이다.</p>
</blockquote>
<p>관계형 데이터베이스는 테이블 형태로 데이터를 저장하며, 테이블은 행과 열로 구성되어 있다.
주로 웹 어플리케이션에 사용되며, 사용의 용이성과 속도, 안정성이 뛰어나다는 특징이 있다.</p>
<h2 id="postgresql-vs-mysql">PostgreSQL vs MySQL</h2>
<p>위에서 각각의 특징은 살펴보았고, 어떤 데이터 베이스를 선택해야할까?
우선 PostgreSQL은 읽기-쓰기 작업, 대규모 데이터 세트 및 복잡한 쿼리를 관리하는데 선호되고, MySQL은 읽기 성능에 강점을 갖고 있다. 따라서 어느 한쪽이 좋다고 단정짓기는 어렵다. 오래된 자료이긴 하지만 속도 또한 단정짓기 어렵다. <a href="https://wskills.blogspot.com/2007/01/postgresql-vs-mysql-benchmark.html">Windows Skills</a>는 MySQL이 더 빠르다했고, <a href="https://benchw.sourceforge.net/benchw_results_open3.html">Benchw</a>은 PostgreSQL이 더 빠르다고 말했다. </p>
<h2 id="결론">결론</h2>
<p>데이터베이스 디자인, 하드웨어 환경, 사용목적에 따라 적절한 데이터베이스를 선택해야한다. 또한 두 시스템 간의 SQL문법, 데이터 타입이 약간 다르다고 하니 이것 또한 고려해봐야할 것이다.</p>
<hr>
<h2 id="참고링크">참고링크</h2>
<p><a href="https://www.integrate.io/ko/blog/postgresql-vs-mysql-which-one-is-better-for-your-use-case-ko/">PostgreSQL, MySQL 속도 비교</a>
<a href="https://f-lab.kr/insight/mysql-vs-postgresql">MySQL과 PostgreSQL의 차이점 및 선택 가이드</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 이미지, 글꼴 최적화]]></title>
            <link>https://velog.io/@seobi-97/React-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EA%B8%80%EA%BC%B4-%EC%B5%9C%EC%A0%81%ED%99%94</link>
            <guid>https://velog.io/@seobi-97/React-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EA%B8%80%EA%BC%B4-%EC%B5%9C%EC%A0%81%ED%99%94</guid>
            <pubDate>Wed, 24 Apr 2024 05:02:28 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-발생">문제 발생</h2>
<p>회사 홈페이지 리뉴얼 작업중에 문제가 발생했다.
큰 문제는 아니지만 이미지, 글꼴 로드에 많은 소요시간이 발생한다는 점이다.
이로 인해 첫 페이지 진입 시, 이미지가 매우 늦게 보이고 글꼴도 처음에는 일반 글씨였다가 나중에 적용하려던 글씨로 바뀌었다.
홈페이지를 방문하는 사람들에게 매우 불편한 요소로 생각이 되었고, 이를 최적화를 통해 개선해야겠다고 마음먹었다.</p>
<p>아래 사이트를 통해 성능 문제를 진단하였다. 역시 성능이 좋지 않다는 평가를 받았다.
<a href="https://pagespeed.web.dev/">https://pagespeed.web.dev/</a>
<img src="https://velog.velcdn.com/images/seobi-97/post/3e396eab-4ca6-4c46-9020-964de0e0d787/image.png" alt="">
<img src="https://velog.velcdn.com/images/seobi-97/post/42815525-6d85-4b8c-920f-7d5cb83e1d5a/image.png" alt=""></p>
<h2 id="이미지-최적화">이미지 최적화</h2>
<p>이미지 폴더에 들어가 이미지를 하나하나 확인하였다.
페이지에서 사용하는 규격은 훨씬 작은데 사진의 규격은 너무도 컸다.
또한 .jpg, .png 파일을 .webp으로 바꿔줬다.
.webp은 뛰어난 무손실 및 손실 압축을 제공 하는 최신 이미지 파일 포멧으로 모든 경우는 아니지만 대부분의 경우 더 가벼운 용량을 갖는다.
<a href="https://squoosh.app/">해당 사이트</a>를 통해 .webp으로 이미지 변환 작업을 해주었다. 사이즈 또한 변경하였다.
<img src="https://velog.velcdn.com/images/seobi-97/post/99e7ee30-4f31-4926-9800-4a6a08b83bdb/image.png" alt=""><img src="https://velog.velcdn.com/images/seobi-97/post/e224bdaa-aae2-45d1-9e64-bee2429e11ae/image.png" alt=""></p>
<p>모든 사진의 용량을 줄이고, 해당 규격에 맞게만 적용하여도 아래와 같이 성능이 많이 향상된 것을 느낄 수 있다.
<img src="https://velog.velcdn.com/images/seobi-97/post/19b78087-7378-48ec-b4a4-67f0e738f58e/image.png" alt="">
<img src="https://velog.velcdn.com/images/seobi-97/post/0941ebbe-b342-483c-bb52-49d6ff2bdc8b/image.png" alt=""></p>
<h2 id="글꼴-최적화">글꼴 최적화</h2>
<p>리뉴얼 사이트에서는 pretendard 글꼴을 사용한다. 글꼴을 다운받다보니 .ttf, .otf, .woff가 보였다. 처음에 적용한 것은 .otf였다. 글꼴 또한 성능 평가에서 좋지 못했기 때문에 .woff를 적용해보기로 했다.</p>
<h3 id="otf-ttf-woff">OTF, TTF, WOFF</h3>
<p>우선 OTF와 TTF의 차이점은 포멧 방식이다. OTF는 오픈 타입(Open Type Font)이고, TTF는 트루 타입(True Type Font)이다. OTF가 TTF보다 더 많은 정보를 바탕으로 섬세한 작업을 하며 일반 환경에서는 큰 차이가 없지만, 고해상도에서는 차이가 있다.
WOFF는 압축된 파일이라 다운이나 웹 페이지에서 사용이 용이하다는 장점이 있다.</p>
<h3 id="성능-비교">성능 비교</h3>
<p>.woff 파일을 적용하여 테스트를 진행했는데 큰 차이를 느끼지 못했다. 여러 이미지들과 함께 글꼴을 불러오다보니 차이를 느끼지 못한 것 같다. 또한 성능 비교를 하면서 생각이 든게 Pretendard에 Light, Bold, Regular, Medium 4개를 다 사용할 필요가 있냐는 거다. 4개의 글꼴 파일을 다 불러오다보니 성능이 저하될 수 밖에 없었다. 따라서 2개의 글꼴 파일만 사용하고 나머지는 font-weight를 통해 적절히 변경하기로 하였다.</p>
<h3 id="preload">Preload</h3>
<p>글꼴 최적화 방법 중 preload라는 것을 발견하였고, 해당 방법으로 테스트하였다. 하지만 preload도 어쨌든 load를 해야한다. 4개의 글꼴을 모두 preload를 하게 되면 첫 페이지를 띄우는 시간이 엄청 증가할 것이다. 따라서 2개의 글꼴을 사용하고 처음 화면에 사용되는 글꼴만 preload를 해주었다.
.html파일에서 head 태그 안에 입력해주었다.</p>
<pre><code>&lt;link
    rel=&quot;preload&quot;
    type=&quot;font/woff&quot;
    as=&quot;font&quot;
    href=&quot;/src/assets/fonts/pretendard/Pretendard-Medium.woff&quot;
    crossorigin
/&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Organization Repository에 Github label 커스텀 적용하기+ 템플릿 생성하기]]></title>
            <link>https://velog.io/@seobi-97/Organization-Repository%EC%97%90-Github-label-%EC%BB%A4%EC%8A%A4%ED%85%80-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0-%ED%85%9C%ED%94%8C%EB%A6%BF-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@seobi-97/Organization-Repository%EC%97%90-Github-label-%EC%BB%A4%EC%8A%A4%ED%85%80-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0-%ED%85%9C%ED%94%8C%EB%A6%BF-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 24 Nov 2023 07:54:37 GMT</pubDate>
            <description><![CDATA[<p>이번 프로젝트에 Issues와 Pull requests를 좀 더 체계적으로 관리하고,  템플릿을 만들어 편리하게 이용도 하고 이쁘게 꾸미고 싶었다.</p>
<h1 id="github-label-커스텀-적용하기">Github label 커스텀 적용하기</h1>
<p>구글에서 Github issue template을 검색하는 도중 label 간편하게 커스텀 할 수 있는 <a href="https://github.com/Financial-Times/github-label-sync">github-label-sync</a>라는 라이브러리를 발견했다.</p>
<p>Github에서 Issues에 들어가면, 기본적으로 label 9개가 주어진다.
하지만 이 라이브러리를 사용하면 이렇게 더 이쁘고 아이콘으로 한눈에 확인하기 편하다.
<img src="https://velog.velcdn.com/images/seobi-97/post/ca698abc-b524-447b-8cb6-c8f0fe95996e/image.png" alt=""></p>
<h2 id="1-labelsjson">1. labels.json</h2>
<p>자신이 원하는 경로에 labels.json 파일을 만든다.
label의 <code>name</code>, <code>color</code>, <code>description</code>, <code>aliases</code> 를 정의할 수 있다.</p>
<pre><code>[
  {
    &quot;name&quot;: &quot;⚙ Setting&quot;,
    &quot;color&quot;: &quot;e3dede&quot;,
    &quot;description&quot;: &quot;개발 환경 세팅&quot;
  },
  {
    &quot;name&quot;: &quot;✨ Feature&quot;,
    &quot;color&quot;: &quot;a2eeef&quot;,
    &quot;description&quot;: &quot;기능 개발&quot;
  },
  {
    &quot;name&quot;: &quot;🌏 Deploy&quot;,
    &quot;color&quot;: &quot;C2E0C6&quot;,
    &quot;description&quot;: &quot;배포 관련&quot;
  },
  {
    &quot;name&quot;: &quot;🎨 Html&amp;css&quot;,
    &quot;color&quot;: &quot;FEF2C0&quot;,
    &quot;description&quot;: &quot;마크업 &amp; 스타일링&quot;
  },
  {
    &quot;name&quot;: &quot;🐞 BugFix&quot;,
    &quot;color&quot;: &quot;d73a4a&quot;,
    &quot;description&quot;: &quot;Something isn&#39;t working&quot;
  },
  {
    &quot;name&quot;: &quot;💻 CrossBrowsing&quot;,
    &quot;color&quot;: &quot;C5DEF5&quot;,
    &quot;description&quot;: &quot;브라우저 호환성&quot;
  },
  {
    &quot;name&quot;: &quot;📃 Docs&quot;,
    &quot;color&quot;: &quot;1D76DB&quot;,
    &quot;description&quot;: &quot;문서 작성 및 수정 (README.md 등)&quot;
  },
  {
    &quot;name&quot;: &quot;📬 API&quot;,
    &quot;color&quot;: &quot;D4C5F9&quot;,
    &quot;description&quot;: &quot;서버 API 통신&quot;
  },
  {
    &quot;name&quot;: &quot;🔨 Refactor&quot;,
    &quot;color&quot;: &quot;f29a4e&quot;,
    &quot;description&quot;: &quot;코드 리팩토링&quot;
  },
  {
    &quot;name&quot;: &quot;🙋‍♂️ Question&quot;,
    &quot;color&quot;: &quot;9ED447&quot;,
    &quot;description&quot;: &quot;Further information is requested&quot;
  },
  {
    &quot;name&quot;: &quot;🥰 Accessibility&quot;,
    &quot;color&quot;: &quot;facfcf&quot;,
    &quot;description&quot;: &quot;웹접근성 관련&quot;
  },
  {
    &quot;name&quot;: &quot;✅ Test&quot;,
    &quot;color&quot;: &quot;ccffc4&quot;,
    &quot;description&quot;: &quot;test 관련(storybook, jest...)&quot;
  }
]</code></pre><h2 id="2-access-token-발급하기">2. Access-token 발급하기</h2>
<p><a href="https://github.com/">github</a> 사이트에 로그인을 한 후, 자신의 계정에서 Settings-&gt;Developer Settings에 들어간다.
Personal access tokens에 들어가 토큰을 발급받는다. 토큰값은 <code>ghp</code>로 시작하고 나중에 따로 확인하기 어려울 수 있으므로, 다른 곳에 복사해두길 바란다.</p>
<h2 id="3-github-label-sync-설치">3. github-label-sync 설치</h2>
<p>터미널에 명령어를 입력해 <a href="https://github.com/Financial-Times/github-label-sync">github-label-sync</a> 라이브러리를 설치한다.
나는 mac 권한 설정 때문에, 앞에 <code>sudo</code>을 추가로 입력했다.</p>
<pre><code>npm install -g github-label-sync</code></pre><h2 id="4-label을-레포지토리에-적용">4. label을 레포지토리에 적용</h2>
<p>아래는 github-label-sync의 명령어 가이드이다.</p>
<pre><code>Usage: github-label-sync [options] &lt;repository&gt;

Options:

  -h, --help                  output usage information
  -V, --version               output the version number
  -a, --access-token &lt;token&gt;  a GitHub access token (also settable with a GITHUB_ACCESS_TOKEN environment variable)
  -l, --labels &lt;path&gt;         the path or URL to look for the label configuration in. Default: labels.json
  -d, --dry-run               calculate the required label changes but do not apply them
  -A, --allow-added-labels    allow additional labels in the repo, and don&#39;t delete them</code></pre><p>보통은 아래와 같이 입력한다. 하지만 나는 Organization Repository에 적용해야 했기에 사용자 계정 이름 대신 Organization을 넣었다.</p>
<pre><code>github-label-sync --access-token token --labels ./labels.json 사용자 계정 이름/저장소 이름</code></pre><h1 id="github-issues-template-만들기">Github issues template 만들기</h1>
<p>Issues template을 만들어두면 이슈를 생성할 때, 시간을 절약할 수 있고 형식이 갖춰질 수 있다.</p>
<h2 id="1-템플릿-생성하기">1. 템플릿 생성하기</h2>
<p>해당 레포지토리 Settings에 들어간다.
General탭에서 내리다보면 <code>Set up templates</code>라는 초록색 버튼이 있다.
<code>Bug report</code>, <code>Feature request</code>, <code>Custom template</code> 세 가지 유형이 존재하는데, 나는 <code>Custom template</code>를 선택했다.</p>
<h2 id="2-자유롭게-템플릿-편집하기">2. 자유롭게 템플릿 편집하기</h2>
<p>해당 템플릿의 연필 모양 아이콘을 클릭한다.
원하는 이슈 제목과 템플릿 설명, 템플릿 내용을 입력한다.
나는 아래와 같이 입력했다.</p>
<pre><code>## Description
{ 업무에 대한 요약 및 설명 }

&lt;br/&gt;

## CheckList

{ 작업 체크리스트 }
- [ ] 리스트 1
- [ ] 리스트 2

&lt;br/&gt;

## ETC
{ 기타 내용, 의존성있는 작업 }</code></pre><p>짜잔 완성됐다.
앞으로 이슈를 작성할 때 해당 템플릿을 사용할 수 있고, 안에 내용만 직접 작성해주면 된다.
<img src="https://velog.velcdn.com/images/seobi-97/post/d3fa4f31-b5cd-4f0d-a975-f026bc6994d8/image.png" alt=""></p>
<h1 id="출처">출처</h1>
<blockquote>
<p><a href="https://www.thegeekdiary.com/github-label-sync-command-examples/">&quot;github-label-sync&quot; 명령 예</a>
<a href="https://velog.io/@rimo09/Github-github-label-%ED%95%9C%EB%B2%88%EC%97%90-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0">[Github] github label 한번에 적용하기</a>
<a href="https://velog.io/@sasha1107/%EA%B9%83%ED%97%88%EB%B8%8C-%EC%9D%B4%EC%8A%88-%ED%85%9C%ED%94%8C%EB%A6%BF-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0">깃허브 이슈 템플릿 생성하기</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Organization Repository를 vercel+git actions로 무료 배포하기]]></title>
            <link>https://velog.io/@seobi-97/Organization-Repository%EB%A5%BC-vercelgit-actions%EB%A1%9C-%EB%AC%B4%EB%A3%8C-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@seobi-97/Organization-Repository%EB%A5%BC-vercelgit-actions%EB%A1%9C-%EB%AC%B4%EB%A3%8C-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 23 Nov 2023 07:56:55 GMT</pubDate>
            <description><![CDATA[<p>팀 프로젝트를 진행하는데 프론트쪽을 미리 배포를 하고 진행을 하려고 했다.
여러가지 방법으로 배포가 가능하지만 비교적으로 편리한 <a href="https://vercel.com/">Vercel</a>을 이용하기로 했다.</p>
<h2 id="github-repositories-불러오기">Github Repositories 불러오기</h2>
<p>Vercel 페이지를 들어가서 로그인을 하면 이 페이지가 보일 것이다. 다른 페이지가 보인다면 Add new...을 클릭 후 드롭다운 메뉴 중에 project 클릭하면 이 화면이 나타날 것이다.</p>
<p><img src="https://velog.velcdn.com/images/seobi-97/post/aee867ad-d6cd-4e3d-a5c0-9702a954895e/image.png" alt="">
개인 프로젝트, 레포지토리를 불러오려면 그냥 검색을 해서 import 버튼을 클릭하면 된다. 하지만 나는 Organization에 있는 레포지토리를 불러오려했기에 Add Github Account를 클릭하여 불러왔다.
Organization owner면 상관없는데, 일반 Member일 경우 owner에게 메일이 가서 따로 승인을 받아야했다.
<img src="https://velog.velcdn.com/images/seobi-97/post/e653ca19-9235-4344-94d1-bbcc60690a61/image.png" alt=""></p>
<p>이후 팀 이름, 프로젝트 이름, 도메인 등을 설정해주고 deploy를 눌러준다. 다른 블로그를 봤을때 바로 배포가 됐는데 나는 아무 반응이 없어서 안된줄 알았다. 그래서 계속 시도를 하니 이미 존재하는 프로젝트라고 나와서 개인 페이지로 돌아가봤는데 프로젝트가 만들어져있었다. 아직 배포된 상태는 아니였고, master branch에 push를 하니까 배포가 시작되었다.</p>
<p>정상적으로 배포가 되었지만 문제가 하나 있었다.
Pro요금제가 적용된다는 점..!
아쉽게도 평가판에서만 가능하고, 이후에는 요금을 따로 지불해야했다.</p>
<p>그래서 다른 방법이 없을까? 고민을 하다가 바로 좋은 <a href="https://velog.io/@rmaomina/organization-vercel-hobby-deploy">블로그</a>를 발견했다.</p>
<p>블로그의 내용을 간단하게 설명하자면, 내 개인 레포지토리를 만들고 vercel과 연동을 한다. 그리고 배포하고 싶은 Organization 레포지토리에 Git Actions를 적용하여 내 개인 레포지토리로 push하여 그 내용이 배포가 되는것이다.</p>
<h2 id="배포-작업을-위해-해야할-일">배포 작업을 위해 해야할 일</h2>
<ol>
<li><p>개인 레포지토리를 생성해준다.(public)</p>
</li>
<li><p>vercel에 개인 레포지토리를 연동한다.</p>
</li>
<li><p>Organization 레포지토리 루트 경로로 <code>build.sh</code> 파일을 생성해준다.
<code>[team-repo-name]</code>을 모두 지우고 Organization 레포지토리 이름을 입력해준다. </p>
<p> #!/bin/sh
 cd ../
 mkdir output
 cp -R ./[team-repo-name]/* ./output
 cp -R ./output ./[team-repo-name]/</p>
</li>
<li><p>Organization 레포지토리에 secret 변수를 등록해준다.
<code>EMAIL</code>에는 Github 계정 이메일을, <code>AUTO_ACTIONS</code>에는 개인 계정에서 발급받은 token값을 넣어준다. token값을 만들때는 repo 권한을 선택해주고, 다시 확인하기 어려울 수 있으므로 다른 곳에 복사해두길 바란다. token값은 <code>ghp_</code>가 들어가는 값이다.
<img src="https://velog.velcdn.com/images/seobi-97/post/63dfd8e6-86de-4729-a5d5-4d73f7e42156/image.png" alt=""></p>
</li>
<li><p>GitHub Actions 활성화 &amp; 워크플로우 생성</p>
<pre><code> name: git push into another repo to deploy to vercel

 on:
       push:
         branches: [master]

 jobs:
       build:
         runs-on: ubuntu-latest
         container: pandoc/latex
         steps:
               - uses: actions/checkout@v2
               - name: Install mustache (to update the date)
             run: apk add ruby &amp;&amp; gem install mustache
               - name: creates output
             run: sh ./build.sh
               - name: Pushes to another repository
             id: push_directory
             uses: cpina/github-action-push-to-another-repository@main
             env:
                   API_TOKEN_GITHUB: ${{ secrets.AUTO_ACTIONS }}
             with:
                   source-directory: &quot;output&quot;
                   destination-github-username: &quot;your-github-name&quot;
                   destination-repository-name: &quot;your-project-name&quot;
                   user-email: ${{ secrets.EMAIL }}
                   commit-message: ${{ github.event.commits[0].message }}
                   target-branch: main
               - name: Test get variable exported by push-to-another-repository
             run: echo $DESTINATION_CLONED_DIRECTORY</code></pre></li>
</ol>
<p>Organization 레포지토리에서 push하는 branch가 master이기 때문에 바꿔줬다. 하지만 아래 target-branch는 개인 레포지토리의 main branch이기 때문에 main으로 설정했다.</p>
<p>처음 생성할땐 actions/checkout@v2으로 작성했는데 현재 2024년 11월 기준 <a href="https://github.com/actions/checkout">최신 버전</a>은 v4이다.</p>
<p>아까 secret 변수로 등록했던 token값과 Email을 불러와주고, <code>your-github-name</code> <code>your-project-name</code>에 해당 값을 넣어준다.</p>
<p>나는 이 파일을 Github 사이트에서 Actions -&gt; New workflow를 통해서 생성했다. 생성하고 Actions에 들어가면 작동하고 있는지 확인이 가능하다.</p>
<p>성공적으로 작동이 된다면 개인 레포지토리로 파일들이 잘 옮겨졌는지, vercel에 제대로 배포가 되었는지 확인해보자!</p>
<p>우선 나는 배포를 잘 확인했지만, 프로젝트를 진행하면서도 계속 확인해야겠다.
끝!</p>
<blockquote>
<h3 id="출처">출처</h3>
<p><a href="https://velog.io/@rmaomina/organization-vercel-hobby-deploy">GitHub Organization 프로젝트를 vercel 무료로 연동하기 (+git actions)</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Backbone.js란 무엇일까?]]></title>
            <link>https://velog.io/@seobi-97/Backbone.js</link>
            <guid>https://velog.io/@seobi-97/Backbone.js</guid>
            <pubDate>Tue, 24 Oct 2023 00:58:35 GMT</pubDate>
            <description><![CDATA[<h2 id="backbonejs란">Backbone.js란?</h2>
<blockquote>
<p>Backbone is a library, not a framework, and plays well with others. - backbonejs.org</p>
</blockquote>
<p>Backbone은 프론트엔드 자바스크립트 코드의 틀을 만드는데 도움을 주는 경량 자바스크립트 라이브러리이다.
프레임워크와 라이브러리의 차이는 제어 흐름을 누가 가지냐의 차이이다.
프레임워크는 프레임워크가, 라이브러리는 사용자가 가지고 있다.</p>
<p>Backbone은 틀을 만드는데 도움이 되는 메서드를 제공해주지만 틀을 만드는건 순전히 개발자의 몫이다.
틀을 제시하기 때문에 프레임워크라고 하는 사람도 있다.</p>
<h2 id="backbone의-특징">Backbone의 특징?</h2>
<p>Backbone의 특징은 아래와 같다.</p>
<ul>
<li>MVC 패턴을 따르게 도와준다. MVC 패턴과 유사하지만, 컨트롤러 기능이 뷰에 포함된다.
데이터를 모델로 표현하여 생성, 검증, 삭제 및 서버에 저장할 수 있다.</li>
<li>라이브러리라서 확장성이 좋다. 꼭 따라야할 규칙이 minimal하면서 RESTful API를 잘 지원하다보니 다른 프레임워크에도 잘 녹아들 수 있다. 따라서 임베디드 위젯부터 대형 웹 애플리케이션까지 사용 가능하다.</li>
<li>경량이기 때문에 네트워크가 느린 경우에도 충분히 사용할 수 있으며 학습에 시간이 적게 걸린다.</li>
</ul>
<h2 id="backbone을-사용해야-할-경우">Backbone을 사용해야 할 경우</h2>
<ol>
<li><p>서버에 대한 HTTP 요청을 줄여야하는 경우
 Backbone 같은 MVC 프레임워크는 대게 모든 스크립트, 스타일시트가 포함된 묶음을 한 번 다운로드하고 필요사항을 마크업하고 나서, 백그라운드에 추가적으로 필요한 작업을 수행하는식으로 동작한다. 그래서 SPA를 만드는데 유용하다.</p>
</li>
<li><p>복잡한 UI 설계를 단순화해야할 경우
 Backbone은 하나의 view를 이용해서 요청된 모델을 공유하는 여러 개의 sub-view를 관리할 수 있다.</p>
</li>
<li><p>스파게티 코드를 피하고 싶은 경우
 Backbone은 개발자가 비즈니스 모델과 인터페이스를 분리하게끔 도와준다. Model과 View간에 event-driven communication을 할 수 있도록 설계되어있기 때문이다.</p>
</li>
</ol>
<h2 id="모델과-뷰">모델과 뷰</h2>
<p><img src="https://velog.velcdn.com/images/seobi-97/post/56d857b6-3d69-476c-a6e9-bc91061df92b/image.png" alt=""></p>
<p>모델은 데이터 속성의 내부 테이블을 관리하고 데이터가 수정되면 변경 이벤트를 트리거한다. 모델을 일반적으로 지원 데이터베이스가 있는 REST API인 지속성 레이어를 사용하여 데이터 동기화를 처리한다.</p>
<p>뷰는 사용자 인터페이스의 원자 덩어리이다. 특정 모델 또는 여러 모델의 데이터를 렌더링하는 경우가 많지만, 뷰는 독립된 데이터 없는 UI 덩어리일 수도 있다. 모델은 일반적으로 뷰를 인식하지 않아야한다. 대신 뷰는 모델의 변경 이벤트를 수신하고 적절하게 다시 반응하거나 다시 렌더링한다.</p>
<h2 id="컬렉션">컬렉션</h2>
<p><img src="https://velog.velcdn.com/images/seobi-97/post/20d9d7b7-4754-48f4-8db8-a052142b4553/image.png" alt="">
컬렉션은 관련 모델 그룹을 처리하고, 새 모델을 서버에 로드 및 저장하고, 모델 목록에 대한 집계 또는 계산을 수행하기 위한 도우미 기능을 제공하는데 도움이 된다. 자체 이벤트외에도 컬렉션은 컬렉션 내의 모델에 발생하는 모든 이벤트를 프록시하여 컬렉션의 모든 모델에 발생할 수 있는 모든 변경 사항을 한 곳에서 수신할 수 있다.</p>
<blockquote>
<p>출처
<a href="https://backbonejs.org/#Getting-started">https://backbonejs.org/#Getting-started</a>
<a href="https://humblego.tistory.com/14">https://humblego.tistory.com/14</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[메인 프로젝트 회고]]></title>
            <link>https://velog.io/@seobi-97/%EB%A9%94%EC%9D%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@seobi-97/%EB%A9%94%EC%9D%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Wed, 02 Aug 2023 14:46:44 GMT</pubDate>
            <description><![CDATA[<p>조금 늦은 회고 시작~~
7월 27일 데모데이가 끝나고 일주일 간 푹 쉬었다.
바로 회고를 작성해야했는데 미루다가 이제 적어본다.
코드와 기술에 대해서는 따로 다루고 이번 회고에서는 프로젝트를 하면서 도움이 되었던 활동에 대해서 이야기를 해보겠다.
(주의!) 사진이 아주 많을 예정!
<img src="https://velog.velcdn.com/images/seobi-97/post/cc570b72-8e60-401e-9a7b-d1768230427b/image.png" alt="">
데모데이 부스에 모인 우리 팀원들!
팀장이라 처음이라 어리숙한 부분이 많았는데 다들 한달 간 정말 고생 많았다.</p>
<h2 id="매일-아침-스크럼">매일 아침 스크럼</h2>
<p><img src="https://velog.velcdn.com/images/seobi-97/post/9a5966b8-4808-41a6-aa4c-6f7e1d370cdf/image.png" alt="">
<img src="https://velog.velcdn.com/images/seobi-97/post/d787481a-bdba-4884-b052-fa25fef4c75d/image.png" alt=""></p>
<p>아무도 빠짐없이 정말 잘 지켜졌던 것 중에 하나이다. 프로젝트에 대해 소통하는 것에 많은 도움이 되었다. 간단하게 작업 상황에 대해 적고 한명씩 돌아가면서 발표를 하고, 그에 대해 궁금한 점들을 자유롭게 질문하고 답하였다. 자신의 작업을 신경쓰느라 다른 사람들의 진행 상황에 대해 무지하거나 놓칠 수 있는 부분이 있는데, 매일 같이 진행하다보니 그런 부분은 없었다.</p>
<h2 id="다양한-문서-작업">다양한 문서 작업</h2>
<p><img src="https://velog.velcdn.com/images/seobi-97/post/ce491536-d02f-43f5-9390-7a6deea6bce9/image.png" alt=""></p>
<p>혼자 프로젝트를 진행할때는 한번도 작성해보지 않았다. 이런 걸 작성해야 한다고도 못 느꼈다. 하지만 이번에 작성을 해보면서 문서 작업도 상당히 중요하다고 느꼈다. 개발자인 우리를 위해서, 사용자들을 위해서 중요하다 생각한다. 초기에 기획했던 것과 얼만큼 적합한가? 개발 중 API명세서를 참고해서 개발을 하기 등 많은 도움이 되었다. 문서 작성에도 거의 일주일이란 시간을 들여 열심히 작업했었다.</p>
<h2 id="멘토링">멘토링</h2>
<p><img src="https://velog.velcdn.com/images/seobi-97/post/a3138cbc-4dc8-4cce-b976-49099e0288e8/image.png" alt="">
처음 멘토님과 개발 기술 스택에 관해 얘기를 나눴을때다. 이때는 사실 좀 어지러웠다. 마치 기술면접 현장에 있는 듯한 느낌이었다. 하지만 이 스택을 왜 사용해야 하는지에 대해 더 명확하게 판단할 수 있는 시간이었다. 남들이 많이 쓰니까, 요즘 뜨는 기술이니까 그냥 선택 부분도 많았기 때문이다. 그래서 필요한 부분만 가져다 썼다.
<img src="https://velog.velcdn.com/images/seobi-97/post/6599229f-28bc-4890-ab92-24b6ea181462/image.png" alt="">
한달 중 4번의 멘토링이 이루어졌는데 프로젝트에 관해서, 개발자로 성장하는거에 관해서 모두 도움이 많이 되었다. 좋은 개발자로 성장하기 바라는 멘토님이 좋은 말씀을 많이 해주셨다. hoc패턴 공부와 AccessToken을 쿠키에 저장해 관리하는 법은 나중에 꼭 공부해 적용시켜야겠다.
클린 코드라는 책은 꼭 읽어볼거다!</p>
<h2 id="마지막으로">마지막으로</h2>
<p>한달 간 9시부터 18시까지 회의 및 작업한다는 일은 정말 쉽지 않았다. 스트레스도 많이 받고, 살도 많이 찌는 기간이었다. 하지만 끝나고 보니 너무 뿌듯하고 많은 것을 배웠다고 생각한다. 또한 AccessToken 쿠키로 저장 및 관리, 다크 모드, 비디오 추가 등 다양한 기능을 더 추가하고 싶은 욕심이 있다. 우선 아래 배포 링크를 올려놨으니 음악을 감상하고 싶은 분들은 한번 들어가보셔도 좋을 것 같다!
<img src="https://velog.velcdn.com/images/seobi-97/post/8bdad544-1b7c-40a3-af7d-05effda6c255/image.png" alt=""></p>
<p><a href="https://on.petpil.site">https://on.petpil.site</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Pre-Project 회고]]></title>
            <link>https://velog.io/@seobi-97/Pre-Project-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@seobi-97/Pre-Project-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Tue, 27 Jun 2023 05:28:14 GMT</pubDate>
            <description><![CDATA[<p>드디어 Pre-Project가 끝났다.
프로젝트 하나가 끝났다는 기쁨과 Main-Project가 바로 앞에 있다는 절망감이 공존한다.
프로젝트를 하면서 내가 아직 많이 부족하구나 느꼈다.
그동안 프로젝트 경험도 해봤고, 전공자이기 때문에 자신감이 있었는데 생각만큼 잘 해내지 못한거 같다.</p>
<p><img src="https://velog.velcdn.com/images/seobi-97/post/9f9c1788-924b-4694-8db1-34ab9add28fe/image.PNG" alt=""></p>
<h2 id="git-이슈">Git 이슈</h2>
<p>제일 아찔한 순간이 아닐까싶다.
한번은 Pull Request를 Main으로 날렸는데 모르고 merge를 해버렸다.
그래서 원래 팀 계획은 모두 작업을 완료하고 dev브랜치에서 main브랜치로 옮기고 배포하기였다. 하지만 이 일을 계기로 중간중간 main브랜치로 올리는 형식으로 바꼈다. 파일들이 날라간 건 아니여서 다행이다.</p>
<p>한번은 dev에서 새로운 브랜치를 생성하고 작업한 다음 commit 메시지만 작성했다. 그리고 팀 레포 dev내용을 개인 레포 dev에 pull을 하고 작업하던 브랜치에서 merge했다. 마지막으로 다시 작업하고 바뀐 내용들을 commit하고 push를 했는데 처음 작성한 commit 메시지가 다 재커밋 되는 상황이 벌어졌다. 거의 100개...ㄷㄷ 당황하고 되돌려보려했지만 이미 pr후 merge까지 완료라 바꿀 수는 없었다. 이번에도 마찬가지로 파일들이 날라간 건 아니여서 다행이었다.</p>
<h2 id="테스트-이슈">테스트 이슈</h2>
<p>처음부터 더미 데이터를 잘 구성하고 남겨놨어야 되는데, 일찍 제거를 해버려서 문제였다. 내가 맡은 답변부분이 로그인, 질문 생성부터 타고 넘어오는 부분이여서 앞부분이 완벽하게 작성된 상태여야 작업을 하기 편한 상황이다. 하지만 우리는 동시에 작업을 진행하고 있었고, 이런 부분을 미리 대비를 했어야했다.</p>
<p>또한 내 노트북에는 인텔리제이가 있고, 서버를 한번 실행시켰을때 잘 작동되길래 이걸 계속 pull하면서 이용해야지라는 생각이 있었다. 하지만 백엔드분들도 계속 작업중이고 그에 맞는 작업 환경 변화도 있을것이다. 그래서 편하게 localhost로 서버를 실행시키려는 생각도 실패했다. 몸이 편하려고하니 벌 받았다. 그래서 계속 pull할때 마다 오류가 자주 떴다. 이렇게 실행이 안될때마다 백엔드 분에게 요청해서 해결하길 반복했다. 이 시간이 생각보다 너무 많이 쓰였다.</p>
<h2 id="코로나-이슈">코로나 이슈</h2>
<p>프로젝트 중간에 갑자기 목이 아프고 감기기운이 있길래
전날 저녁에 괜히 선풍기를 틀고 잤나했다. 하지만 3일동안 낫지 않아서 자가키트를 검사했더니 양성이 나왔다.
그동안 눈도 가렵고, 긁으면 다래끼처럼 부어서 약을 잘 못 처방 받았나했는데 눈병을 동반한 신종 코로나였다. 이름도 아르크투루스이다. 그리스 고대학자 이름 같다.
이틀은 열나고 좀 아팠고, 일찍 회복해서 다행이었다.</p>
<h2 id="마지막-느낀점">마지막 느낀점</h2>
<p>그래도 다른분들과 작업을 하면서 많은 것을 느꼈다. 소통하는 법, 작업량 관리 및 공유 등등..
다음 Main-Project에서는 작업전에 좀 더 계획을 세부적으로 잘 짜보고 실행에 옮겨야겠다.
<strong>아자아자 화이팅!</strong>
<img src="https://velog.velcdn.com/images/seobi-97/post/d47d8185-40ef-4422-9440-da6e8611729c/image.PNG" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CORS & Proxy]]></title>
            <link>https://velog.io/@seobi-97/CORS-Proxy</link>
            <guid>https://velog.io/@seobi-97/CORS-Proxy</guid>
            <pubDate>Wed, 07 Jun 2023 06:51:04 GMT</pubDate>
            <description><![CDATA[<h2 id="cors">Cors</h2>
<h3 id="cors란">Cors란?</h3>
<blockquote>
<p><strong>Cors란?</strong></p>
</blockquote>
<p>교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제입니다.</p>
<h3 id="출처">출처</h3>
<blockquote>
<p>웹 콘텐츠의 출처(origin)는 접근할 때 사용하는 URL의 스킴(프로토콜), 호스트(도메인), 포트로 정의됩니다. 두 객체의 스킴, 호스트, 포트가 모두 일치하는 경우 같은 출처를 가졌다고 말합니다.
일부 작업은 동일 출처 콘텐츠로 제한되나, CORS를 통해 제한을 해제할 수 있습니다.</p>
</blockquote>
<h2 id="proxy">Proxy</h2>
<h3 id="proxy란">Proxy란?</h3>
<blockquote>
<p>프록시(Proxy)는 &quot;대리&quot;의 의미로, 인터넷과 관련해서 쓰이는 경우, 특히 내부 네트워크에서 인터넷 접속을 할 때에, 빠른 액세스나 안전한 통신등을 확보하기 위한 중계서버를 &quot;프록시 서버&quot;라고 일컫는다. 클라이언트와 Web서버의 중간에 위치하고 있어, 대신 통신을 받아 주는 것이 프록시 서버이다. </p>
</blockquote>
<h3 id="proxy-적용-전-흐름">Proxy 적용 전 흐름</h3>
<p><img src="https://velog.velcdn.com/images/seobi-97/post/4331cb66-6194-421c-ae5f-7219be42f5dc/image.png" alt="">
위의 그림은 proxy를 적용해 브라우저를 속이기 전 흐름입니다. 프론트엔드, 즉 여러분이 개발한 React 앱에서 브라우저 쪽으로 요청을 보냅니다. 그러면 브라우저는 백엔드, 즉 서버 쪽으로 리소스를 요청하게 됩니다. 이때 접근 권한이 있는지, 즉 출처가 같은지 확인하는데 이때 백엔드 서버는 정상적으로 200 OK 응답을 브라우저에게 보냅니다. 마지막으로 브라우저는 받은 리소스 및 응답과 함께 출처가 같은지 아닌지 확인하게 되는데, 이때 출처가 다르다면 응답을 파기(CORS Error) 하고, 출처가 같다면 응답을 파기하지 않고 다시 프론트엔드 쪽으로 응답을 보내주는 것입니다.</p>
<h3 id="proxy-적용-후-흐름">Proxy 적용 후 흐름</h3>
<p><img src="https://velog.velcdn.com/images/seobi-97/post/39fb097d-2251-4c34-89a8-6a0b911e1444/image.png" alt="">
위의 그림은 proxy를 적용해 브라우저를 속인 후 흐름입니다. React 앱에서 브라우저를 통해 API를 요청할 때, proxy를 통해 백엔드 서버로 요청을 우회하여 보내게 됩니다. 그러면 백엔드 서버는 응답을 React 앱으로 보내고, React 앱은 받은 응답을 백엔드 서버 대신 브라우저에게 전달합니다. 이렇게 되면 출처가 같아지기 때문에 브라우저는 이 사실을 눈치채지 못하고 허용하게 됩니다.</p>
<h3 id="proxy를-설정해-http-통신하기">Proxy를 설정해 HTTP 통신하기</h3>
<h4 id="webpack-dev-server-proxy">webpack dev server proxy</h4>
<p>webpack dev server의 proxy를 사용하게 되면, 브라우저 API를 요청할 때 백엔드 서버에 직접적으로 요청을 하지 않고, 현재 개발서버의 주소로 우회 요청을 하게 됩니다. 그러면 웹팩 개발 서버에서 해당 요청을 받아 그대로 백엔드 서버로 전달하고, 백엔드 서버에서 응답한 내용을 다시 브라우저 쪽으로 반환합니다.</p>
<pre><code>//package.json
//보통 맨 밑에 작성한다.
...
&quot;browserslist&quot;: {
    &quot;production&quot;: [
      &quot;&gt;0.2%&quot;,
      &quot;not dead&quot;,
      &quot;not op_mini all&quot;
    ],
    &quot;development&quot;: [
      &quot;last 1 chrome version&quot;,
      &quot;last 1 firefox version&quot;,
      &quot;last 1 safari version&quot;
    ]
  },
    &quot;proxy&quot; : &quot;우회할 API 주소&quot;
}</code></pre><p>그리고 기존의 fetch, 혹은 axios를 통해 요청하던 부분에서 도메인 부분을 제거합니다.</p>
<pre><code>export async function getAllfetch() {

    const response = await fetch(&#39;우회할 api주소/params&#39;);
    .then(() =&gt; {
            ...
        })
}

export async function getAllfetch() {

    const response = await fetch(&#39;/params&#39;);
    .then(() =&gt; {
            ...
        })
}</code></pre><h4 id="react-proxy-사용법">React Proxy 사용법</h4>
<p>우선 http-proxy-middleware 라이브러리 설치한다.</p>
<p><code>npm install http-proxy-middleware --save</code></p>
<p>그 이후 src폴더 안에 setupProxy.js 파일을 생성하고, 설치한 라이브러리를 불러온 후, 아래와 같이 작성한다.</p>
<pre><code>const { createProxyMiddleware } = require(&#39;http-proxy-middleware&#39;);

module.exports = function(app) {
  app.use(
    &#39;/api&#39;, //proxy가 필요한 path prameter를 입력합니다.
    createProxyMiddleware({
      target: &#39;http://localhost:5000&#39;, //타겟이 되는 api url를 입력합니다.
      changeOrigin: true, //대상 서버 구성에 따라 호스트 헤더가 변경되도록 설정하는 부분입니다.
    })
  );
};</code></pre><p>또한 위와 동일하게 기존 fetch, axios를 통해 요청하던 부분에서 도메인 부분을 제거한다.</p>
<pre><code>export async function getAllfetch() {

    const response = await fetch(&#39;우회할 api주소/params&#39;);
    .then(() =&gt; {
            ...
        })
}

export async function getAllfetch() {

    const response = await fetch(&#39;/params&#39;);
    .then(() =&gt; {
            ...
        })
}</code></pre><h4 id="오류">오류</h4>
<p>작업을 하다보니
<code>Uncaught (in promise) SyntaxError: Unexpected token &#39;&lt;&#39;, &quot;&lt;!DOCTYPE &quot;... is not valid JSON</code>
이런 오류가 발생하였다.</p>
<p><a href="https://stackoverflow.com/questions/37269808/react-js-uncaught-in-promise-syntaxerror-unexpected-token-in-json-at-posit">스택오버플로우</a>, <a href="https://xionwcfm.tistory.com/340">블로그</a>에서 찾아보니 노드 버전이나 여러 요인으로 인해 프록시 서버가 정상적으로 동작하지 않는다는 것이였다.</p>
<p>직접 노드 버전을 다운그레이드 하거나 <code>.env</code>파일을 생성한 후
<code>DANGEROUSLY_DISABLE_HOST_CHECK=true</code>을 작성해주면 해결이 되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript 문법(열거형, 타입 별칭, 타입 추론, 클래스)]]></title>
            <link>https://velog.io/@seobi-97/TypeScript-%EB%AC%B8%EB%B2%95%EC%97%B4%EA%B1%B0%ED%98%95-%ED%83%80%EC%9E%85-%EB%B3%84%EC%B9%AD-%ED%83%80%EC%9E%85-%EC%B6%94%EB%A1%A0-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@seobi-97/TypeScript-%EB%AC%B8%EB%B2%95%EC%97%B4%EA%B1%B0%ED%98%95-%ED%83%80%EC%9E%85-%EB%B3%84%EC%B9%AD-%ED%83%80%EC%9E%85-%EC%B6%94%EB%A1%A0-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Wed, 31 May 2023 07:50:11 GMT</pubDate>
            <description><![CDATA[<h2 id="열거형">열거형</h2>
<p>TypeScript에서 열거형은 다음과 같은 형태로 정의한다.</p>
<pre><code>enum Color {
  Red,
  Green,
  Blue,
}</code></pre><p>Color라는 열거형을 정의하고 있다. 열거형의 값은 <code>Red</code>,<code>Green</code>,<code>Blue</code> 세 개입니다.</p>
<h3 id="숫자형-열거형enum">숫자형 열거형(Enum)</h3>
<p>열거형은 숫자형과 문자열형, 혹은 이 둘의 조합으로 정의될 수 있다. 디폴트 값으로 숫자형을 사용하며, 각 값은 자동으로 0부터 시작하여 1씩 증가한다. 그러나 수동으로 값을 지정할 수도 있다.</p>
<pre><code>enum Color {
  Red = 1,
  Green = 2,
  Blue = 4,
}</code></pre><p>열거형의 값에 대해 산술 연산을 수행할 수도 있다.</p>
<pre><code>enum Color {
  Red = 1,
  Green = 2,
  Blue = 4,
}

let c: Color = Color.Green;
let greenValue: number = Color.Green;
let blueValue: number = Color.Blue;

console.log(c);          // 출력: 2
console.log(greenValue);  // 출력: 2
console.log(blueValue);   // 출력: 4</code></pre><p>열거형은 일반적으로 상수값을 대신하여 사용되므로, 타입스크립트에서는 열거형이 많이 사용됩니다. 열거형은 코드를 더욱 가독성 높게 만들어주고, 오타와 같은 실수를 방지해 줍니다.</p>
<h3 id="문자형-열거형enum">문자형 열거형(Enum)</h3>
<p>문자형 열거형은 앞에서 살펴본 숫자형 열거형과 개념적으로는 거의 비슷합니다. 문자형 열거형은 열거형의 값을 전부 다 특정 문자 또는 다른 열거형 값으로 초기화해야 합니다.</p>
<pre><code>enum Direction {
  Up = &quot;UP&quot;,
  Down = &quot;DOWN&quot;,
  Left = &quot;LEFT&quot;,
  Right = &quot;RIGHT&quot;,
}

let myDirection: Direction = Direction.Up;
console.log(myDirection); // 출력: &quot;UP&quot;</code></pre><p>문자형 열거형에는 숫자형 열거형과는 다르게 auto-incrementing이 없습니다. 대신 디버깅을 할 때 숫자형 열거형의 값은 가끔 불명확하게 나올 때가 있지만 문자형 열거형은 항상 명확한 값이 나와 읽기 편합니다.</p>
<p>예를 들어, HTTP 요청 방식을 나타내는 열거형을 정의할 수 있다.</p>
<pre><code>enum HttpMethod {
  Get = &quot;GET&quot;,
  Post = &quot;POST&quot;,
  Put = &quot;PUT&quot;,
  Delete = &quot;DELETE&quot;,
}

function makeRequest(url: string, method: HttpMethod) {
  // ...
}

makeRequest(&quot;/api/data&quot;, HttpMethod.Post);</code></pre><p>위 코드에서는 HTTP 요청 방식을 나타내는 <code>HttpMethod</code> 열거형을 정의하고 있습니다. <code>makeRequest</code> 함수는 URL과 HTTP 요청 방식을 인자로 받습니다. HTTP 요청 방식을 지정할 때는 <code>HttpMethod.Post</code>와 같이 열거형 값을 사용합니다.</p>
<p>이렇게 열거형을 사용하면 오타와 같은 실수를 방지할 수 있으며, 코드의 가독성과 안정성을 높일 수 있습니다.</p>
<h3 id="역-매핑reverse-mappings">역 매핑(Reverse mappings)</h3>
<p>역 매핑은 숫자형 열거형에만 존재하는 특징입니다. 열거형의 키(<code>key</code>)로 값(<code>value</code>)을 얻을 수 있고 값(<code>value</code>)으로 키(<code>key</code>)를 얻을 수도 있습니다.</p>
<pre><code>enum Enum {
    A
}
let a = Enum.A;
let nameOfA = Enum[a]; // &quot;A&quot;</code></pre><p>위와 같이 열거형의 키로 값을 얻을 수 있지만, 값으로도 열거형의 키를 얻을 수 있습니다. 이는 숫자형 열거형에만 존재하며, 문자형 열거형에서는 존재하지 않는 특징입니다.</p>
<h2 id="타입-별칭type-aliases">타입 별칭(Type Aliases)</h2>
<blockquote>
<p><strong>타입 별칭(Type Aliases)이란?</strong></p>
</blockquote>
<p>타입의 새로운 이름을 만드는 것이다.
이는 새로운 이름으로 기존의 타입을 참조하는 것을 의미한다.
타입 별칭을 이용하여 타입의 새로운 이름을 만들 때 키워드 <code>type</code>을 사용하여 작성한다.</p>
<p>예시를 통해 살펴보자.</p>
<pre><code>type MyString = string;

let str1: string = &#39;hello!&#39;;

// string 타입처럼 사용할 수 있습니다.
let str2: MyString = &#39;hello world!&#39;;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript 프로젝트 환경 구성하기]]></title>
            <link>https://velog.io/@seobi-97/TypeScript-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%84%B1%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@seobi-97/TypeScript-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%84%B1%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 30 May 2023 07:53:16 GMT</pubDate>
            <description><![CDATA[<h2 id="typescript-프로젝트-환경-구성하기">TypeScript 프로젝트 환경 구성하기</h2>
<ol>
<li>TypeScript를 적용하기 전, 먼저 프로젝트 폴더 하나를 생성하자.<pre><code>mkdir (폴더명)
cd (폴더명)</code></pre></li>
</ol>
<p>터미널에서 생성해줘도 되고, 직접 폴더를 만들어도 된다.</p>
<ol start="2">
<li><p>프로젝트 폴더를 생성했으면 프로젝트 폴더 안으로 이동하여, <code>npm init -y</code>명령어를 실행해 새로운 프로젝트를 초기화한다.</p>
</li>
<li><p>이제 프로젝트 내부에서 npm을 사용할 준비가 되었으므로, 이제 TypeScript를 설치한다.</p>
<pre><code>npm install typescript --save-dev</code></pre></li>
<li><p>프로젝트 루트 디렉토리에 <code>tsconfig.json</code>파일을 생성한다.
 그리고 아래 코드를 붙여준다.</p>
<pre><code>//tsconfig.json
//compilerOptions 내의 속성은 자유롭게 커스텀 할 수 있습니다.
{
&quot;compilerOptions&quot;: {
 &quot;target&quot;: &quot;es6&quot;,
 &quot;module&quot;: &quot;commonjs&quot;,
 &quot;sourceMap&quot;: true,
 &quot;outDir&quot;: &quot;./dist&quot;
},
&quot;include&quot;: [
 &quot;src/**/*&quot;
]
}
</code></pre></li>
<li><p>이제 <code>src</code>폴더 밑에 TypeScript언어로 된 파일을 작성할 수 있습니다.</p>
</li>
</ol>
<h3 id="typescript-eslint와-prettier-설정하기">TypeScript ESLint와 Prettier 설정하기</h3>
<p>TypeScript는 2020년까지만 TSLint를 지원하고, 이후부터는 typescript-eslint를 사용해야 합니다. 만일 TypeScript 프로젝트에서 ESLint나 Prettier를 사용하고 싶다면, 아래의 안내를 따릅니다.</p>
<ol>
<li>VSCode 에디터를 사용할 것이므로, 확장 프로그램인 ESLint를 설치합니다.
<img src="https://velog.velcdn.com/images/seobi-97/post/9c544a4f-f316-4c61-9ed2-dbb7e94f1e4b/image.png" alt=""></li>
<li>그리고 VSCode 에디터에 다음 설정을 적용합니다.
윈도우는 <code>ctrl+shift+p</code>, 맥은 <code>cmd+shift+p</code>
<code>Preference: Open User Settings (JSON)</code> 입력한다.<pre><code>{
// ... 
&quot;editor.codeActionsOnSave&quot;: {
   &quot;source.fixAll.eslint&quot;: true
},
&quot;eslint.alwaysShowStatus&quot;: true,
&quot;eslint.workingDirectories&quot;: [
   {&quot;mode&quot;: &quot;auto&quot;}
],
&quot;eslint.validate&quot;: [
   &quot;javascript&quot;,
   &quot;typescript&quot;
],
}</code></pre>위의 내용은 사용자 설정에 넣어야 하며, 기존에 설치한 확장 프로그램들이 있다면 안이 채워져 있을 수 있습니다. 채워져 있다면 그 밑에 바로 내용을 넣어주시면 됩니다.</li>
</ol>
<ol start="3">
<li><p>마지막으로 VSCode 에디터 설정 중 format on save가 설정되어 있는지 확인합니다. 되어 있다면 설정을 해제합니다.</p>
</li>
<li><p>확장 프로그램 ESLint, Prettier을 설치한다.</p>
</li>
<li><p>몇 가지 프리셋과 라이브러리를 설치한다.</p>
<pre><code>npm i -D @babel/core @babel/preset-env @babel/preset-typescript @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint prettier eslint-plugin-prettier</code></pre></li>
</ol>
<ol start="6">
<li>프로젝트 폴더 밑에 <code>.eslintrc.js</code> 파일을 만들고 이하 내용을 붙여 넣습니다.<pre><code>module.exports = {
root: true,
env: {
 browser: true,
 node: true,
 jest: true,
},
extends: [
 &#39;plugin:@typescript-eslint/eslint-recommended&#39;,
 &#39;plugin:@typescript-eslint/recommended&#39;,
],
plugins: [&#39;prettier&#39;, &#39;@typescript-eslint&#39;],
rules: {
 &#39;prettier/prettier&#39;: [
   &#39;error&#39;,
   {
     singleQuote: true,
     tabWidth: 2,
     printWidth: 80,
     bracketSpacing: true,
     arrowParens: &#39;avoid&#39;,
   },
 ],
 &#39;@typescript-eslint/no-explicit-any&#39;: &#39;off&#39;,
 &#39;@typescript-eslint/explicit-function-return-type&#39;: &#39;off&#39;,
 &#39;prefer-const&#39;: &#39;off&#39;,
},
parserOptions: {
 parser: &#39;@typescript-eslint/parser&#39;,
},
};</code></pre></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript란?]]></title>
            <link>https://velog.io/@seobi-97/TypeScript%EB%9E%80</link>
            <guid>https://velog.io/@seobi-97/TypeScript%EB%9E%80</guid>
            <pubDate>Tue, 30 May 2023 07:37:17 GMT</pubDate>
            <description><![CDATA[<h2 id="typescript란">TypeScript란?</h2>
<blockquote>
<p><strong>TypeScript란?</strong></p>
</blockquote>
<p>자바스크립트의 슈퍼셋인 오픈소스 프로그래밍 언어이다.
마이크로소프트에서 개발, 유지하고 있으며 엄격한 문법을 지원한다.
타입스크립트에서 자신이 원하는 타입을 정의하고 프로그래밍을 하면 자바스크립트로 컴파일되어 실행할 수 있다.</p>
<h3 id="typescript의-장점">TypeScript의 장점</h3>
<p>TypeScript를 꼭 써야하는 이유가 있을까?
그 이유가 있다면 무엇일까?</p>
<h4 id="1-에러-예방">1. 에러 예방</h4>
<p>타입스크립트는 타입을 부여하기 때문에 코드 작성 시에 알맞지 않은 타입을 넣거나, 혹은 타입을 넣지 않았을 경우 사전에 IDE에서 오류라고 경고를 준다.
<img src="https://velog.velcdn.com/images/seobi-97/post/d83895bd-b60b-44f5-abf6-38a0db9e0507/image.png" alt="">
이것은 사전에 오류를 디버깅 할 수 있고 나중에 디버깅하는 시간을 줄여줘 생산적인 측면에서 효과적이다.</p>
<h4 id="2-실행-속도">2. 실행 속도</h4>
<p>타입스크립트는 개발자가 코드를 작성하면서 직접 오류를 체크해 수정을 하기도 하고, 타입을 미리 결정하기 때문에 기계가 할 일을 덜어 실행 속도가 매우 빠르다.</p>
<h4 id="3-안정성--협업용이성">3. 안정성 &amp; 협업용이성</h4>
<p>자바스크립트로만 코드를 작성하게 되면 나중에 같이 협업하는 사람이나 다른 사람이 코드를 읽을 경우 모든 변수의 타입을 짐작하기는 쉽지 않다. 자기 자신 또한 나중에 코드를 보게 될때 바로 알아내기 쉽지 않을 것이다.</p>
<p>하지만 타입스크립트는 타입을 명시할 수 있고 컴파일 시 오류를 찾기 때문에 보다 안정적이다.</p>
<h4 id="4-슈퍼셋">4. 슈퍼셋</h4>
<p>타입스크립트는 자바스크립트의 슈퍼셋, 즉 자바스크립트의 +알파이다.</p>
<p>자바스크립트와의 100%호환이 되고, 그 이외에도 클래스, 인터페이스 등 객체향 프로그래밍 패턴을 제공한다.</p>
<h3 id="typescript의-단점">TypeScript의 단점</h3>
<h4 id="1-초기-세팅">1. 초기 세팅</h4>
<p>자바스크립트에 비해 초기 세팅이 까다롭다.</p>
<h4 id="2-새로운-언어">2. 새로운 언어</h4>
<p>자바스크립트를 이용할 줄 아는 개발자라도 새로운 언어를 따로 배워 적용해야한다.</p>
<h4 id="3-더-많은-코드-작성">3. 더 많은 코드 작성</h4>
<p>타입스크립트는 타입을 일일이 명시해줘야 하기 때문에 자바스크립트보다 더 많은 코드를 작성해야한다.</p>
<h2 id="느낀점">느낀점</h2>
<p>공부를 새로 해야한다는 단점이 있지만, 자바스크립트보다 타입스크립트를 선호하는 추세기도 하고 장점들을 살펴보니 쓸 이유가 충분하다!
타스 공부 화이팅~!</p>
<br>

<p>출처: <a href="https://fomaios.tistory.com/entry/TS-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8TypeScript%EB%9E%80">https://fomaios.tistory.com/entry/TS-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8TypeScript%EB%9E%80</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Section 3 회고]]></title>
            <link>https://velog.io/@seobi-97/Section-3-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@seobi-97/Section-3-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Tue, 09 May 2023 06:22:10 GMT</pubDate>
            <description><![CDATA[<h2 id="저번-회고록-리뷰">저번 회고록 리뷰</h2>
<p>원래 하루에 계획을 머릿속으로만 생각하고 진행하는 편이었다.
파워 P 답게 할 수 있는거 좀  하고 나머지는 나중에 해야지라는 마인드로 넘겨왔는데, 그게 쌓이고 쌓이다보니 진짜 많아졌다.
이제는 진짜 체계적으로 해야겠다는 생각이 들었다.
그렇다고 직접 어디에 기록하는 건 귀찮아서 Todo앱을 깔아 하루하루 공부, 운동 계획을 세우고 지켜나가는 중이다!</p>
<h2 id="목표-상기하기">목표 상기하기</h2>
<blockquote>
<ul>
<li>일주일 계획을 세우고 지키는 체계적인 공부</li>
</ul>
</blockquote>
<ul>
<li>질문이 들어왔을때, 바로 대답할 수 있는 깊은 언어 지식 보유</li>
<li>건강한 몸 상태 유지</li>
</ul>
<h2 id="keep-problem-작성하기">Keep, Problem 작성하기</h2>
<h3 id="keep">Keep</h3>
<blockquote>
<ul>
<li>새로 알게 된 사이트, 정보 노션 및 블로그에 기록하기</li>
</ul>
</blockquote>
<ul>
<li>일주일 2~3회 러닝 및 마라톤 참가(빵빵런, JTBC 마라톤 추천!)</li>
</ul>
<h3 id="problem">Problem</h3>
<blockquote>
<ul>
<li>집중력을 좀 더 기르자!</li>
</ul>
</blockquote>
<ul>
<li>좀 더 우선순위를 확실하게 정하고 그에 맞게 공부하자.</li>
</ul>
<h2 id="try-작성하기">Try 작성하기</h2>
<blockquote>
<ul>
<li>빨리 해버리고 쉬자는 마인드 장착하기(매우매우 어려움)</li>
</ul>
</blockquote>
<ul>
<li>새로 알게 된 내용을 나만의 생각으로 블로그 작성하기</li>
<li>채용공고 수시 확인하기</li>
</ul>
<h2 id="우선순위">우선순위</h2>
<blockquote>
<ul>
<li>우선 기본 과제에 충실하자.</li>
</ul>
</blockquote>
<ul>
<li>스터디 공부 및 기록</li>
<li>개인 공부</li>
</ul>
<h2 id="마무리">마무리</h2>
<p>전체적으로 살펴보니 더 나아진 점도 있었고, 더 나빠진 점도 있었다.
나빠진 점을 빠르게 파악해서 인정하고 고치려고 노력해보자!</p>
]]></description>
        </item>
    </channel>
</rss>