<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>_u__me_with.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 03 Dec 2024 07:19:17 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>_u__me_with.log</title>
            <url>https://velog.velcdn.com/images/_u__me_with/profile/ef548088-5ef8-4e55-8db3-221827dfdd3b/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. _u__me_with.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/_u__me_with" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Vercel에서 PostgreSQL 연결하기(설정 방식 변경)]]></title>
            <link>https://velog.io/@_u__me_with/Vercel%EC%97%90%EC%84%9C-PostgreSQL-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0%EC%84%A4%EC%A0%95-%EB%B0%A9%EC%8B%9D-%EB%B3%80%EA%B2%BD</link>
            <guid>https://velog.io/@_u__me_with/Vercel%EC%97%90%EC%84%9C-PostgreSQL-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0%EC%84%A4%EC%A0%95-%EB%B0%A9%EC%8B%9D-%EB%B3%80%EA%B2%BD</guid>
            <pubDate>Tue, 03 Dec 2024 07:19:17 GMT</pubDate>
            <description><![CDATA[<p><strong>Next.js 튜토리얼 공식문서</strong>를 통해 학습을 진행하던 중 <code>ch.6 Setting Up Your Database</code> 과정에서 약간의 문제가 생겼던걸 정리해보고자한다.
현재 시점 이후에 학습하고있는 동료들 중에서 나와같은 일이 벌어진 사람이 있다면 도움이 되었으면 좋겠다.</p>
<p>Vercel에서 Git 연동으로 배포 후 데이터베이스 생성 과정부분이다.</p>
<h2 id="문제-상황">문제 상황</h2>
<p>Create a Postgres database 을 진행하던 중
<img src="https://velog.velcdn.com/images/_u__me_with/post/f8f637ec-0300-4056-99c1-d77c4205220d/image.png" alt="">
이 이미지를 보면 Postgres 데이터베이스를 선택하여 데이터베이스를 만들면 된다.
하지만 이런식으로 옵션이 아예 없었다.
<img src="https://velog.velcdn.com/images/_u__me_with/post/b86cd72f-de0f-42ac-bce2-43641e70707c/image.png" alt=""></p>
<h2 id="원인">원인</h2>
<p>이전에는 Vercel에서 직접 PostgreSQL을 연결할 수 있었지만 <strong>2024.11월부터</strong> <code>모든 새로운 PostgreSQL 데이터베이스는 Neon을 통해 생성</code>해야하며, 기존 데이터베이스는 Neon으로 자동 이전된다.
이처럼 서비스가 바뀌었던것...!!!</p>
<blockquote>
<p>참고
<a href="https://vercel.com/docs/storage/vercel-postgres">https://vercel.com/docs/storage/vercel-postgres</a></p>
</blockquote>
<h2 id="해결">해결</h2>
<p>Neon를 통해 데이터베이스를 생성하면 문제없이 될 것이다.
<img src="https://velog.velcdn.com/images/_u__me_with/post/7e8feae3-51dc-41bb-9410-0caf8f843656/image.png" alt="">
👉🏻 Neon을 선택 후 continue 클릭한다.</p>
<p>Region을 보면 Korea가 없을 것이다. 사실 크게 중요한 부분은 아니지만 거리에 따라 속도에 영향을 줄 수 있어서 <strong>가까운 지역</strong>으로 선택하면 된다.
<img src="https://velog.velcdn.com/images/_u__me_with/post/7c0e03ba-7835-4ccc-800c-859324952050/image.png" alt="">
👉🏻 한국과 가장 가까운 싱가포르를 선택 후 나머지는 그대로 적용하고 continue를 클릭한다.
<img src="https://velog.velcdn.com/images/_u__me_with/post/d999a7cb-8e04-4577-80d5-eb5f93b2fac2/image.png" alt="">
👉🏻Datebase Name은 원하는 걸로 설정한 후 create 를 클릭하면 데이터베이스 만들기 성공!</p>
<h2 id="추가">추가</h2>
<p>그 후 Next.js 튜토리얼 과정을 보면 seed 과정이 있는데 그 코드의 한 부분을 보면</p>
<pre><code class="language-javascript">export async function GET() {
  // return Response.json({
  //   message:
  //     &#39;Uncomment this file and remove this line. You can delete this file when you are finished.&#39;,
  // });
  try {
    await client.sql`BEGIN`;
    await seedUsers();
    await seedCustomers();
    await seedInvoices();
    await seedRevenue();
    await client.sql`COMMIT`;

    return Response.json({ message: &#39;Database seeded successfully&#39; });
  } catch (error) {
    await client.sql`ROLLBACK`;
    return Response.json({ error }, { status: 500 });
  }
}</code></pre>
<p>이 코드가 정상작동하는 코드이다.
주석 처리된 부분도 함께 살펴보면 <strong>return이 실행되면 함수는 종료</strong>하게 된다. 그래서 try 문 안으로 진입하지 못하기 때문에 기존 코드에서 첫번째 return를 주석하거나 삭제해서 seed 할 수 있는 코드로 만들어줘야한다.</p>
<blockquote>
<p>참고
<a href="https://hyeyeong1011.github.io/2020-03-11-post37/">https://hyeyeong1011.github.io/2020-03-11-post37/</a>
<a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/return">https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/return</a></p>
</blockquote>
<h2 id="마무리하며">마무리하며</h2>
<p>개발 환경은 언제든지 변경되고 업데이트 된다. 항상 학습하며 배워가는 자세를 유지하도록 노력하자!!는 다짐을 한 번 더 할 수 있는 계기가 되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React에서 state를 직접 변경하지않고 setState(useState)를 사용하는 이유]]></title>
            <link>https://velog.io/@_u__me_with/React%EC%97%90%EC%84%9C-state%EB%A5%BC-%EC%A7%81%EC%A0%91-%EB%B3%80%EA%B2%BD%ED%95%98%EC%A7%80%EC%95%8A%EA%B3%A0-setStateuseState%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0</link>
            <guid>https://velog.io/@_u__me_with/React%EC%97%90%EC%84%9C-state%EB%A5%BC-%EC%A7%81%EC%A0%91-%EB%B3%80%EA%B2%BD%ED%95%98%EC%A7%80%EC%95%8A%EA%B3%A0-setStateuseState%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0</guid>
            <pubDate>Tue, 26 Nov 2024 15:16:27 GMT</pubDate>
            <description><![CDATA[<h2 id="state는-무엇인가">state는 무엇인가?</h2>
<p>state는 간단히 말하면 <strong>변수</strong>이다. 하지만 일반 변수와 다르게 <strong>값이 변하면 렌더링</strong>이 일어난다. 리액트는 컴포넌트 외부에 마치 선반에 보관하듯 state를 저장한다. 즉 state의 스냅샷을 저장한다고 생각하면 된다. 여기서 중요한 점은 <strong>state는 객체</strong>라는 것이다!</p>
<blockquote>
<p>참고
<a href="https://ko.react.dev/learn/state-as-a-snapshot">https://ko.react.dev/learn/state-as-a-snapshot</a></p>
</blockquote>
<h2 id="state-setter-함수">state setter 함수</h2>
<p>state 변수를 업데이트하고 리액트에 컴포넌트를 다시 렌더링하도록 유발하는 함수이다.</p>
<h2 id="setstateusestate를-사용해야하는-이유">setState(useState)를 사용해야하는 이유</h2>
<p>컴포넌트는 업데이트가 필요한 경우에만 render 함수를 호출한다. state를 직접 변경하면 리액트가 render 함수를 호출하지않아 상태 변경이 일어나도 렌더링이 일어나지 않을 수 있기 때문이다.</p>
<p><code>render 함수가 호출되는 상황은 언제지????</code>
리액트의 컴포넌트는 생성된 후 마운트 상태에서 한 번 render() 메서드를 실행하고 난 후 <strong>업데이트 상태에 진입</strong>할때만 다시 호출한다.</p>
<p><code>그러면 언제 업데이트 상태로 진입하는데..?</code></p>
<ul>
<li>state, props 가 변경되었을때</li>
<li>Context API를 사용하는 경우 Context값이 변경되었을때</li>
<li>부모 컴포넌트가 렌더링 되었을때</li>
</ul>
<h3 id="❓state가-변경-되었을때-업데이트-상태로-진입한다는데">❓state가 변경 되었을때 업데이트 상태로 진입한다는데??!</h3>
<p>여기에서 다시 살펴봐야할 개념은 state의 저장 방식이 <code>객체</code>라는 것이다. 객체가 변경되었다는 근거는 <strong>객체의 메모리 주소</strong>로 판단한다. 그래서 <strong>객체를 직접 수정하면 메모리 주소가 변하지 않기 때문에</strong> 리액트는 변경이 없다고 판단하여 업데이트 상태로 진입하지 않는것!!!
따라서 직접 변경하지 않고 새로운 객체를 만들어서 할당하는 것으로 변경해야하고 그래서 setState(useState)를 사용하는 것이다.</p>
<h2 id="결론">결론</h2>
<p>state를 직접 변경하면 객체로 저장되는 state의 메모리 주소가 변경되지않았기때문에 React가 업데이트 상태로 인지하지 못하여 렌더링이 발생하지 않는다. 따라서 setState(useState)를 사용해여 새로운 객체를 만들어 할당해줘야한다.</p>
<blockquote>
<p>참고</p>
</blockquote>
<ol>
<li>[리액트 공식 문서]
<a href="https://ko.react.dev/learn/updating-objects-in-state">https://ko.react.dev/learn/updating-objects-in-state</a></li>
<li><a href="https://velog.io/@codns1223/React-state-%EB%B3%80%EA%B2%BD%EC%8B%9C-setState%EB%A1%9C-%EB%B3%80%EA%B2%BD%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0">https://velog.io/@codns1223/React-state-%EB%B3%80%EA%B2%BD%EC%8B%9C-setState%EB%A1%9C-%EB%B3%80%EA%B2%BD%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSR SSR SSG ISR 한방에 정리하기]]></title>
            <link>https://velog.io/@_u__me_with/SSR-CSR-SSG-ISR-%ED%95%9C%EB%B0%A9%EC%97%90-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@_u__me_with/SSR-CSR-SSG-ISR-%ED%95%9C%EB%B0%A9%EC%97%90-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 18 Nov 2024 14:43:36 GMT</pubDate>
            <description><![CDATA[<p>SPA? MPA? CSR? 자주 등장하는 단어들인데 비슷한 모양이라 헷갈려서 정리를 한 번 해볼려고한다.</p>
<h2 id="들어가며">들어가며</h2>
<p>일단 이 단어들은 렌더링에 관련된 단어들이다.
<strong>웹 렌더링(Web Rendering)</strong>이란? 웹 페이지를 사용자에게 보여주는 과정을 의미한다.</p>
<p>그리고 SPA와 MPA에 대해서도 알고 가면 좋은데</p>
<h3 id="spa-single-page-application"><strong>SPA</strong>: Single Page Application</h3>
<p>하나의 HTML 페이지에서 새로운 페이지를 불러오지않고 필요한 부분만 동적으로 컨텐츠를 바꾸는 애플리케이션을 말한다. (<strong>React, Vue, Angular</strong>)</p>
<h3 id="mpa-multiple-page-application"><strong>MPA</strong>: Multiple Page Application</h3>
<p>여러 개의 페이지로 이루어져 있으며 사용자가 페이지를 요청할 때마다 서버에서 렌더링된 HTML를 받아와서 컨텐츠를 변경하는 애플리케이션을 말한다.</p>
<hr>
<p>정리하자면 <code>SPA</code>는 첫 페이지만 서버에서 받아오고 <strong>필요한 부분만 동적</strong>으로 변경하고 <code>MPA</code>는 페이지가 달라질 때마다 <strong>서버에서 렌더링 된 파일</strong>들을 받아온다.</p>
<p>이제 이러한 애플리케이션을 구현하기 위해 쓰이는 렌더링 방식이 CSR, SSR ... 이다!</p>
<h2 id="csrclient-side-rendering">CSR(Client Side Rendering)</h2>
<p>클라이언트 사이드 렌더링으로 웹 브라우저에서 <strong>JavaScript를 사용</strong>하여 웹페이지를 렌더링하는 방식이다. 즉, <code>사용자의 브라우저가 서버로부터 데이터를 받아와서</code> 웹 페이지를 <strong>직접 생성</strong>한다.
<strong>SPA(React, Vue, Angular)가 사용하는 렌더링 방식</strong>을 의미한다. 쉽게 말하자면 <code>렌더링이 클라이언트 측</code>에서 일어나는 방식이다.</p>
<h2 id="ssrserver-side-rending">SSR(Server Side Rending)</h2>
<p>서버 사이드 렌더링으로 <strong>서버에서 HTML을 완성</strong>하여 브라우저로 보내는 방식이다. 즉, 새로운 페이지로 이동할 때마다 <code>서버에서 렌더링을 마친 HTML을 받아와서 렌더링</code>한다.
<strong>MPA</strong>를 구현할 때 사용되며 <code>서버측에서 렌더링</code>을 담당하는 방식이다.</p>
<h2 id="❓궁금한점">❓궁금한점</h2>
<h4 id="q-각각-어떤-페이지를-구현할때-좋은가">Q) 각각 어떤 페이지를 구현할때 좋은가?</h4>
<p><strong>A)</strong> <strong>CSR</strong>: 사용자와의 상호작용이 많은 웹사이트나 검색엔진이 중요하지않은 웹사이트
<strong>SSR</strong>: 동적 콘텐츠가 많은 웹사이트, 콘텐츠가 실시간으로 변경되고 항상 최신 상태의 웹사이트</p>
<h4 id="q-내-생각에는-대부분의-웹페이지가-콘텐츠가-실시간으로-바뀌지않나">Q) 내 생각에는 대부분의 웹페이지가 콘텐츠가 실시간으로 바뀌지않나?</h4>
<p><strong>A)</strong> 내가 콘텐츠가 실시간으로 바뀐다고 생각했던 것은 사실 사용자와의 상호작용이 많은 것과 헷갈렸던 부분이다.
정리하자면 <code>CSR</code>은 <strong>소셜 미디어나 주식 차트</strong> 등과 같이 <code>사용자의 행동이 즉시 반영</code>되고 <code>실시간 데이터에 따라 변경</code>되어야하는 웹사이트에 적합하고 <code>SSR</code>은 <strong>뉴스 웹사이트, 블로그, 전자상거래 상품 페이지</strong> 등 동적 콘텐츠가 <code>페이지 요청마다 갱신</code>되어야하는 경우에 적합하다.</p>
<hr>
<p>따라서 이 두 강점을 모두 활용하기 위해 <strong>Next.js 프레임워크</strong>를 사용하게 된 것이다~!</p>
<h2 id="ssgstatic-site-generation">SSG(Static Site Generation)</h2>
<p>정적 사이트 생성으로 빌드 시에 모든 페이지를 HTML로 미리 생성하는 방식이다. 즉 매 요청마다 HTML을 재사용한다. 따라서 회사, 제품 소개(마케팅 페이지), 블로그 게시물과 같이 요청마다 동일한 정보로 반환하는 경우에 사용된다.</p>
<h2 id="isrincremental-static-regeneration">ISR(Incremental Static Regeneration)</h2>
<p>증분 정적 재생성으로 빌드 시점에 페이지를 미리 생성하면서도 특정 시간 간격으로 페이지를 재생성하여 업데이트된 데이터를 반영할 수 있게 한다. 즉, 일부페이지는 자주 업데이트되고 다른 일부 페이지는 그렇지 않은 경우에 사용할 수 있다.</p>
<h2 id="마무리하며">마무리하며</h2>
<p>이번 정리를 통해 Next.js를 사용하게 된 이유도 알게 되었고 헷갈렸던 개념을 확실히 정리할 수 있었다. 렌더링되는 과정을 이해하는 것이 코드를 구현하는데 더 효율적으로 다가갈 수 있다고 생각하여 중요하게 느껴진다. 렌더링 되는 과정을 생각하며 거기에 맞는 기술을 더 공부하고 사용해봐야겠다.</p>
<hr>
<blockquote>
<p>출처 및 참고
<a href="https://yong-nyong.tistory.com/86">https://yong-nyong.tistory.com/86</a>
<a href="https://www.enjoydev.life/blog/frontend/5-csr-ssr">https://www.enjoydev.life/blog/frontend/5-csr-ssr</a>
<a href="https://enjoydev.life/blog/nextjs/1-ssr-ssg-isr">https://enjoydev.life/blog/nextjs/1-ssr-ssg-isr</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹 개발 과정 새싹(SeSAC) 전체 회고록]]></title>
            <link>https://velog.io/@_u__me_with/%EC%83%88%EC%8B%B9-%ED%9A%8C%EA%B3%A0%EB%A1%9D</link>
            <guid>https://velog.io/@_u__me_with/%EC%83%88%EC%8B%B9-%ED%9A%8C%EA%B3%A0%EB%A1%9D</guid>
            <pubDate>Sun, 17 Nov 2024 13:01:21 GMT</pubDate>
            <description><![CDATA[<p>6개월의 교육과정이 끝이 났다. 정말로 6개월이 순식간에 지나갔다. 체감상 아직 반정도 지난거같은데...! 그만큼 교육에 성실히! 열심히 임했다는거겠지? :)
6개월 전과 비교하여 발전한 점, 변화한 점과 앞으로의 계획에 대한 회고를 작성해보겠다!!</p>
<h2 id="하드-스킬의-발전">하드 스킬의 발전</h2>
<h3 id="1-html-css">1. HTML, CSS</h3>
<p><strong>6개월 전</strong>
HTML로 간단한 페이지의 틀을 작성하고, CSS로 기본적인 스타일링을 할 수 있는 정도였다. 태그, 마진, 패딩 같은 <strong>기초 용어만 이해</strong>하고 있었고, 복잡한 구현은 어려웠다.
<strong>6개월 후</strong>
HTML과 CSS가 <strong>프론트엔드 개발의 기본이자 핵심</strong>이라는 것을 깨달았다. 특히 <strong>폼 태그</strong>의 중요성과 다양한 활용법을 익혔으며 CSS 속성을 모두 외우기보다는 <strong>적절히 검색하고 활용</strong>하는 방법을 배우게 되었다. 이를 통해 React 등 더 복잡한 프레임워크에서도 기본기를 활용할 수 있는 자신감을 갖추게 되었다.</p>
<h3 id="2-javascript-typescript">2. JavaScript, TypeScript</h3>
<p><strong>6개월 전</strong>
JavaScript는 단순히 영상을 보며 따라 해본 정도로 투두리스트를 만들어보았었고 TypeScript는 그런 언어가 있구나만 알고 전혀 알지 못했다.
<strong>6개월 후</strong>
<strong>JavaScript DOM</strong>으로 요소를 선택해 동적으로 구현하는 법과 <strong>JQuery, Axios</strong>를 사용하여 백엔드와 연결하는법, <strong>RESTful</strong>로 한 프로젝트를 완성할 수 있는 정도까지 발전하였다. 그리고 <strong>TypeScript</strong>를 <strong>사용하는 이유와 타입을 지정</strong>하여 오류를 줄이고 안정성을 높일 수 있게 되었다.</p>
<h3 id="3-react-redux-toolkit">3. React, Redux Toolkit</h3>
<p><strong>6개월 전</strong>
React로 간단한 블로그를 영상을 보며 따라 만들어본 경험이 있었으며 여러 훅 중 useState만 알고 있었다. Redux와 Redux Toolkit은 전혀 알지 못했다.
<strong>6개월 후</strong>
React를 사용해 하나의 프로젝트를 완성할 수 있게 되었으며 useState는 물론 <strong>useEffect, useCallback, useRef</strong>와 같은 다양한 훅을 활용할 수 있게 되었다. 또한 <strong>React Hook Form</strong>과 같은 라이브러리를 사용해 <strong>폼 데이터를 효율적</strong>으로 관리할 수 있었다. <strong>Redux Toolkit</strong>을 활용해 <strong>전역 상태 관리</strong>도 가능해져, 보다 구조적인 애플리케이션 설계 능력을 갖추게 되었다.</p>
<h3 id="4-nodejs-java-spring-boot">4. Node.js, Java, Spring Boot</h3>
<p><strong>6개월 전</strong>
프론트엔드 개발자로서 백엔드 언어는 중요하지 않다고 생각해 깊이 배우지 않았다.
<strong>6개월 후</strong>
<strong>백엔드와의 원활한 소통</strong>이 프로젝트 완성에 필수적이라는 것을 깨달았다. <strong>RESTful API 구조</strong>를 이해하게 되었으며 <strong>엔드포인트와 HTTP 메서드</strong>의 역할을 분석해 <strong>Axios를 활용한 비동기 통신</strong>을 구현할 수 있게 되었다. 이를 통해 <strong>백엔드와 협업</strong>하며 기능을 통합하는 경험을 쌓았다.</p>
<h2 id="소프트-스킬">소프트 스킬</h2>
<h3 id="1-의사소통능력">1. 의사소통능력</h3>
<p>프로젝트 진행 중 역할 분담과 문제 해결을 위해 <strong>적극적으로 소통</strong>하며 서로 다른 팀원 간의 <strong>의견을 조율하는 방법</strong>을 익혔다. 대면 의사소통뿐만 아니라 <strong>Jira, Slack과 같은 협업 도구</strong>를 활용한 <strong>비대면 소통 방식도 효과적으로 사용</strong>할 수 있게 되었다.</p>
<h3 id="2-협업능력">2. 협업능력</h3>
<p>팀 프로젝트를 통해 <strong>개발자 간의 협업</strong>이 얼마나 중요한지 깊이 체감했다. 특히 <strong>백엔드와의 협업</strong> 과정에서 <strong>API 설계와 데이터 흐름</strong>을 명확히 이해하고 이를 바탕으로 효율적으로 소통하는 방법을 배웠다.</p>
<h3 id="3-문제해결능력">3. 문제해결능력</h3>
<p>개발 과정에서 마주한 크고 작은 문제들을 해결하며 성장할 수 있었다. 공식 문서와 <strong>개발자 커뮤니티</strong>를 적극적으로 활용해 문제를 해결하는 방법을 익혔고 이를 <strong>기록으로 남기는 습관</strong>을 들였다. 또한 <strong>동료 개발자들과 논의</strong>를 통해 함께 해결책을 모색하며 성장의 기회를 만들어냈다.</p>
<h2 id="마무리하며">마무리하며...</h2>
<center><img src="https://velog.velcdn.com/images/_u__me_with/post/cef0e874-d05e-4692-a2c7-0dd53a885c37/image.jpg" width="500"/></center>

<p>6개월 동안 꾸준히 열심히 하다보니 &#39;<strong>우수상</strong>&#39;이라는 노력의 결과물을 받게 되었다. 교육 과정을 통해 개발이 즐겁게 느껴졌고 이를 계기로 꼭 훌륭한 개발자가 되겠다고 결심했다. 특히 <strong>부담없이 질문할 수 있는 동료이자 선배 개발자로 성장</strong>하는 것을 큰 목표로 삼고 있다. 이제 취뽀를 위해 화이팅!!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[트러블 슈팅) React useEffect 로 인한 중복 API 요청 문제 해결 (useCallback Hook으로 최적화까지)]]></title>
            <link>https://velog.io/@_u__me_with/%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%A4%91%EB%B3%B5-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%9B%90%EC%9D%B8%EA%B3%BC-%ED%95%B4%EA%B2%B0%EB%B0%A9%EC%95%88-useCallback%EC%9D%98-%EC%82%AC%EC%9A%A9</link>
            <guid>https://velog.io/@_u__me_with/%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%A4%91%EB%B3%B5-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%9B%90%EC%9D%B8%EA%B3%BC-%ED%95%B4%EA%B2%B0%EB%B0%A9%EC%95%88-useCallback%EC%9D%98-%EC%82%AC%EC%9A%A9</guid>
            <pubDate>Sat, 09 Nov 2024 11:27:15 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-상황">문제 상황</h2>
<p>검색 화면에서 특정 키워드로 검색했을때 동일한 데이터 요청이 두 번 발생하는 문제를 발견하였고 네트워크 탭과 콘솔 로그를 통해 요청 흐름을 추적하였다.</p>
<ul>
<li>검색 버튼을 클릭하면 검색 결과 조회 API 함수가 두 번 호출</li>
<li>네트워크 탭에서 동일한 API 호출이 중복으로 발생한 것을 확인</li>
</ul>
<h2 id="문제-원인-분석">문제 원인 분석</h2>
<p><code>useEffect</code> 의존성 배열 관리</p>
<ul>
<li>페이지(<code>page</code>) 상태 변화에 따라 <code>useEffect</code>가 실행되도록 설정했지만 검색 시 페이지 초기화(<code>setPage(0)</code>)하는 로직이 추가 렌더링을 유발</li>
<li>페이지가 0으로 변경된 후에도 <code>useEffect</code>가 실행되면서 의도치 않은 중복 API 요청 발생</li>
</ul>
<pre><code class="language-typescript">  // 검색 함수
  const handleSearch = async (
    searchTerm: string,
    searchCategory: string,
    sortBy?: SortType
) =&gt; {
    setIsSearching(true);
    setCurrentSearchTerm(searchTerm);
    setSortBy(sortBy);
    setCategory(searchCategory);
    setPage(0); // 검색 시 페이지를 0으로 초기화
    // 이후 useEffect에서 page 변경을 감지하여 추가 렌더링을 유발
    try {
      const reqData: defaultPostReq = {
        category: searchCategory,
        keyword: searchTerm,
        sort: sortBy,
      };
      console.log(&#39;reqData&gt;&gt;&gt;&#39;, reqData);
      const response = await getSearchPlaceList(reqData);
      console.log(&#39;response &gt;&gt;&gt;&#39;, response.data.content);
    }};

useEffect(() =&gt; {
  if (page === 1) return; // page가 0일 때도 API 호출
  // ... API 호출
}, [page, currentSearchTerm, sortBy, category]);</code></pre>
<p><strong>결론</strong>
상태 관리와 데이터 요청 로직이 하나의 컴포넌트에 구현되어있어 상태 변경 시 불필요한 렌더링이 유발되는 것을 확인하였다.</p>
<h2 id="해결-방법">해결 방법</h2>
<ol>
<li><strong>상태 관리와 데이터 요청 로직 분리</strong>
상태 변경에 따른 데이터 요청 로직을 별도의 함수(<code>fetchPlaces</code>)로 분리</li>
<li><strong>의존성 최적화</strong></li>
</ol>
<ul>
<li><code>useCallback</code> 훅을 사용해 <code>fetchPlaces</code> 함수가 의존성 변경 시에만 업데이트되도록 최적화</li>
<li>불필요한 상태 의존성을 제거하고 컴포넌트가 필요한 경우에만 재렌더링되도록 함</li>
</ul>
<pre><code class="language-typescript">// 데이터 패칭 로직을 useCallback으로 감싸기
const fetchPlaces = useCallback(
    async (
      searchTerm: string,
      searchCategory: string,
      sortType: SortType,
      pageNum: number,
      isNewSearch: boolean
    ) =&gt; {
      try {
        setIsLoading(true);

        if (searchTerm) {
          const reqData: defaultPostReq = {
            keyword: searchTerm,
            category: searchCategory,
            sort: sortType,
          };
          const response = await getSearchPlaceList(reqData);
          const newPlaces = response.data.content;

          setPlaces((prev) =&gt;
            isNewSearch ? newPlaces : [...prev, ...newPlaces]
          );
          setHasMore(newPlaces.length === 6);
        } else {
          const result = await getDefaultPlaceList(pageNum - 1, 6);
          const newPlaces = result.data.content;

          setPlaces((prev) =&gt;
            isNewSearch ? newPlaces : [...prev, ...newPlaces]
          );
          setHasMore(newPlaces.length === 6);
        }
      } catch (error) {
        console.error(&#39;데이터 로딩 중 에러:&#39;, error);
        if (isNewSearch) setPlaces(dummyPlaceList);
      } finally {
        setIsLoading(false);
      }
    },
    []
  );</code></pre>
<p>=&gt; 데이터가 패칭되는 로직은 자주 호출되기때문에 useCallback를 사용하여 불필요한 재생성을 막아준다.</p>
<h2 id="배운-점">배운 점</h2>
<ol>
<li><code>useEffect</code>의 의존성 배열에 따라 발생하는 재렌더링의 영향을 명확히 이해하게 되었다.</li>
<li>상태 관리 로직과 데이터 요청 로직이 분리되지 않은 경우 의존성이 복잡해지고 디버깅이 어려워진다는 점을 깨달았다. 특히, 데이터를 비동기로 처리하는 컴포넌트에서는 <strong>하나의 책임</strong>만을 가지도록 설계하는 것이 중요하다는 점을 배웠다.</li>
</ol>
<blockquote>
<p>참고
<a href="https://pinenamu.tistory.com/416">https://pinenamu.tistory.com/416</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[팀 프로젝트 회고록]]></title>
            <link>https://velog.io/@_u__me_with/%ED%8C%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0%EB%A1%9D</link>
            <guid>https://velog.io/@_u__me_with/%ED%8C%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0%EB%A1%9D</guid>
            <pubDate>Fri, 08 Nov 2024 14:05:23 GMT</pubDate>
            <description><![CDATA[<p>새싹 교육 과정 마지막 3차 프로젝트가 끝이 났다. 많은 성장을 한 프로젝트였다. 더 큰 성장을 위해 회고록을 작성해보려한다.</p>
<h2 id="1-프로젝트-소개">1. 프로젝트 소개</h2>
<h3 id="kinderpia">Kinderpia</h3>
<p><strong>진행기간</strong>: 2024.10.21 ~ 11.08
<strong>팀 구성</strong>: 백엔드 (4명), 프론트엔드 (4명)
<strong>주제</strong>: 부모와 아이가 함께하는 문화생활 플랫폼
-&gt; 아이들과 함께 갈만한 문화공간의 정보를 제공하고 같은 관심사를 가진 가족들이 모임을 만들 수 있도록 제공하는 사이트
<strong>기술 스택</strong>:</p>
<ul>
<li><strong>TypeScript</strong>: 정적 타입 검사로 타입의 안정성을 확보</li>
<li>React: 컴포넌트 기반 구조로 재사용성 향상</li>
<li>Sass: 효율적인 스타일 관리</li>
<li><strong>Spring Boot</strong>: 빠른 개발과 설정을 최소화하고 유연한 아키텍처를 제공</li>
<li>JAP: 객체 지향적 데이터 접근 방식으로 효율적인 데이터 관리</li>
<li><strong>JWT</strong>: 정보를 저장할 필요없이 인증에 사용할 수 있음<h2 id="2-내가-맡은-역할">2. 내가 맡은 역할</h2>
포지션: <strong>프론트엔드</strong>
구현 기능:</li>
</ul>
<ol>
<li><p>장소 검색 기능</p>
</li>
<li><p>리뷰 작성, 삭제, 신고</p>
<h2 id="3-회고">3. 회고</h2>
<h3 id="liked">Liked</h3>
<ul>
<li><strong>백엔드와 원활한 소통</strong>
개발 도중 발생한 이슈들을 즉각 공유하고 서로 필요한 데이터를 명확히 전달하면서 효율적인 협업을 진행하였다. 경험을 통해 백엔드와 소통하는 방법을 깨달았다.</li>
<li><strong>협력의 가치 실천</strong>
지난 팀 프로젝트가 끝난 후 혼자 해결하려는 부담에서 벗어나 각자의 역할에 최선을 다하고 필요할 때 도움을 주고받는 협력의 가치를 깨달았고 이번 프로젝트에서 이를 잘 반영하여 함께하는 과정에서 느낄 수 있는 성취감을 느꼈다.</li>
</ul>
</li>
</ol>
<h3 id="lacked">Lacked</h3>
<ul>
<li><strong>데이터 전달의 아쉬움</strong>
각 페이지에서 필요한 데이터를 미리 고려하지 않아 전역 상태 관리를 효과적으로 활용하지 못했다. 실제로 장소상세페이지에서 장소정보, 리뷰 등 여러 컴포넌트로 분리하면서 상태를 props로 전달하는 과정에서 복잡함에 어려움을 있었다.</li>
</ul>
<h3 id="learned">Learned</h3>
<ul>
<li><strong>브라우저 간의 스타일 차이 최소화</strong>
CSS reset 파일을 사용함으로써 일관된 다자인과 스타일을 더 깔끔하게 적용할 수 있는 방법을 알게 되었다.</li>
<li><strong>리더십의 중요성</strong>
다인원 팀으로 진행하면서 확실한 리더십의 중요성을 깨달았다. 많은 사람이 있는 만큼 다양한 의견이 있었고 이를 정리하고 중재하며 결단을 내리는 역할이 필요하다는 것을 절실히 느꼈다.</li>
</ul>
<h3 id="longed-for">Longed for</h3>
<ul>
<li><strong>성능 최적화</strong>
이제는 단순히 기능을 구현하는 데 그치지 않고 성능을 고려하여 최적화된 코드와 효율적인 작업 방식을 적용하고 싶다. 그로 인해 사용자의 경험을 더 향상시킬 수 있도록 노력할 것이다.</li>
<li><strong>테스트 코드 작성</strong>
한 기능을 구현하기 전에 테스트 코드 작성을 통해 버그를 줄이는 방식을 실천해 팀원들과 코드 리뷰를 적극적으로 진행하여 더 나은 결과물을 만들것이다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[DTO와 Domain 차이]]></title>
            <link>https://velog.io/@_u__me_with/DTO-%EC%99%80-domain</link>
            <guid>https://velog.io/@_u__me_with/DTO-%EC%99%80-domain</guid>
            <pubDate>Thu, 17 Oct 2024 13:51:07 GMT</pubDate>
            <description><![CDATA[<p>spring boot와 데이터베이스 연결 코드 학습하면서 DTO와 Domain의 개념을 배우게 되었다. 두 코드가 비슷해보이는데 하는 역할이 다르다. 헷갈리는 부분이 있어 정리해보려고 한다!!</p>
<h2 id="layered-architecture">Layered Architecture</h2>
<p>그 전에 Layered Architecture 구조에 대해 살짝 정리해보자.
애플리케이션을 몇 가지 계층으로 나누어 만드는 아키텍처로 각 계층은 특정 기능에 집중하여 상위 계층은 하위 계층의 구체적인 구현을 알 필요가 없다. 또한 계층 간의 의존성을 최소화하여 유지보수와 확정성을 향상 시킨다.
각 계층의 특징을 간단히 알아보자면!
<img src="https://velog.velcdn.com/images/_u__me_with/post/f4c77373-d515-443e-b4c3-c8a5038c3312/image.png" alt=""></p>
<ol>
<li><strong>Presentation Layer</strong></li>
</ol>
<ul>
<li>스프링 부트의 controller 패키지에 해당</li>
<li>HTTP 요청을 처리하고 응답을 반환 -&gt; 클라이언트 요청 수신 및 파라미터 추출</li>
</ul>
<ol start="2">
<li><strong>Business Layer</strong></li>
</ol>
<ul>
<li>스프링부트의 service 패키지에 해당</li>
<li>복잡한 비즈니스 로직 구현</li>
<li>DTO, Domain 객체간 변환 로직 구현</li>
</ul>
<ol start="3">
<li><strong>Persistence Layer</strong></li>
</ol>
<ul>
<li>스프링부트의 repository 패키지 해당(인터페이스)</li>
<li>데이터베이스와의 상호작용 및 데이터 접근 로직 구현 -&gt; CRUD 연산 수행</li>
</ul>
<ol start="4">
<li><strong>Database Layer</strong></li>
</ol>
<ul>
<li>실제 데이터베이스 시스템</li>
</ul>
<h2 id="dto와-domain의-차이">DTO와 Domain의 차이</h2>
<h3 id="dto란">DTO란?</h3>
<ul>
<li>Data Transfer Object로 계층 간의 데이터 교환을 위한 자바 객체.(= Java Beans)</li>
<li>DB에서 데이터를 얻어 service나 controller로 보낼 때 사용하는 객체. -&gt; <strong>계층 간의 데이터 교환</strong>을 위해 사용
ex) View &lt;- DTO -&gt; Controller &lt;- DTO -&gt; Service</li>
<li><strong>View와 통신하기 위한 클래스.</strong></li>
<li>비동기 처리를 위해 사용</li>
</ul>
<h3 id="domain이란-entity">Domain이란?(= Entity)</h3>
<ul>
<li>DB 테이블에 존재하는 Column들을 필드로 가지는 객체. -&gt; DB 테이블과 1대 1 대응</li>
<li><strong>테이블과 링크될 클래스.</strong></li>
</ul>
<h3 id="dto와-domain를-분리하는-이유">DTO와 Domain를 분리하는 이유</h3>
<ul>
<li>Domain은 테이블 매핑 클래스로 변경 사항이 생기면 다른 여러 클래스에 영향을 주기 때문</li>
<li>DTO은 request/reponse 에 대한 부분이나 View 관련 Presentation Logic을 가지는 자주 변경되는 클래스이다.</li>
<li>즉, DTO는 Domain 을 복사해와서 Presentation Logic을 추가하거나 DTO 상에서만 필요한 필드를 추가, 삭제한 클래스 구조이다.</li>
</ul>
<h2 id="결론">결론</h2>
<p>Layed Architecture는 각 계층 간 책임이 명확히 분리되어있다. 이 특징으로 클라이언트와의 통신이나 서비스 간 데이터 전송을 담당하는 DTO, 데이터베이스와 매핑되는 Domain(Entity)을 분리하여 사용!!!</p>
<h2 id="마무리하며">마무리하며</h2>
<p>두 객체가 비슷해보여 분리해서 사용해야하나? 싶었는데 각 계층의 책임을 분리하기 위하여 사용해야한다는 것을 깨달았다. Domain의 변경이 외부에 영향을 미치지 않고 DTO는 필요에 따라 유연하게 수정할 수 있다는 역할도 확실히 정리하게 되었다. 끝-- <del>(자바 개념 참 어렵다...)</del></p>
<blockquote>
<p>참고 및 출처</p>
</blockquote>
<ol>
<li>[새싹 X 코딩온] 영등포 캠퍼스 6기 입문자도 가능한 웹 개발자 부트캠프 강의 교안</li>
<li><a href="https://youwjune.tistory.com/39">https://youwjune.tistory.com/39</a></li>
<li><a href="https://velog.io/@linger0310/DDD">https://velog.io/@linger0310/DDD</a></li>
<li><a href="https://silverline.tistory.com/134">https://silverline.tistory.com/134</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java와 데이터베이스 연동하는 방식 (Spring JDBC, SQL Mapper, ORM)]]></title>
            <link>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-22%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-Java%EC%99%80-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%97%B0%EB%8F%99%ED%95%98%EB%8A%94-%EB%B0%A9%EC%8B%9D-Spring-JDBC-SQL-Mapper-ORM</link>
            <guid>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-22%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-Java%EC%99%80-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%97%B0%EB%8F%99%ED%95%98%EB%8A%94-%EB%B0%A9%EC%8B%9D-Spring-JDBC-SQL-Mapper-ORM</guid>
            <pubDate>Fri, 11 Oct 2024 14:40:51 GMT</pubDate>
            <description><![CDATA[<p>자바와 데이터베이스 연동하기를 배웠다. 새로운 단어들이 많고 비슷비슷한 기능을 하는거같은데 뭐가 뭐라고? 헷갈리는 개념 3가지를 정리해보겠다!!</p>
<h2 id="java와-데이터베이스-연동하는-방식">Java와 데이터베이스 연동하는 방식</h2>
<ol>
<li><strong>SQL Mapper, ORM</strong>
다른 라이브러리와 스프링을 통합하여 사용하는 방식</li>
<li><strong>스프링 데이터</strong> (Spring Data)
객체 기반의 추상화된 데이터 연동 방식</li>
<li><strong>스프링 JDBC</strong>
SQL 중심의 데이터베이스 연동 방식으로 순수 JDBC를 조금 더 편리하게 사용할 수 있도록 도와주는 도구 </li>
</ol>
<blockquote>
<p>여기서 잠깐 <strong>JDBC란?</strong></p>
</blockquote>
<ul>
<li>자바언어와 데이터베이스를 <strong>연결</strong>해주는 통로</li>
<li>자바에서 데이터베이스에 접근할 수 있도록 해주는 <strong>자바 API</strong></li>
<li>단점: <strong>반복적이고 지루한 코드 작성</strong>이 필요하며 SQL 예외 처리, 리소스관리 등의 <strong>복잡한 작업</strong>을 직접 처리해야함.</li>
</ul>
<h3 id="spring-jdbc-sql-mapper-orm-차이">Spring JDBC, SQL Mapper, ORM 차이</h3>
<p><strong>1. Spring JDBC</strong>
JDBC의 복잡성을 줄이기 위한 스프링 프레임워크의 일부
직접 SQL 쿼리를 작성하고 실행, 저수준의 데이터베이스 연동 방식</p>
<p><strong>2. SQL Mapper</strong>
<strong>MyBatis</strong>가 속한다. (가장 널리 알려짐)
SQL 쿼리와 자바 객체를 매핑하는 방식으로 SQL을 직접 작성하지만 객체와의 매핑을 자동으로 처리한다. 따라서 <strong>데이터베이스</strong> 중심의 개발!!</p>
<p><strong>3. ORM</strong>
<strong>Hibernate</strong>가 속한다. (가장 널리 알려짐)
자바 객체와 데이터베이스 테이블 간의 관계 매핑 방식으로 자바 객체(Entity)가 데이터베이스의 테이블과 어떻게 매핑 될지를 정의한다. 따라서 <strong>객체</strong> 중심의 개발!!</p>
<h2 id="마무리하며">마무리하며</h2>
<p>Spring JDBC, SQL Mapper, MyBatis .... 등등 어떤게 어디에 속하고 어떤게 무슨 역할을 하고 헷갈렸는데 정리가 싹 됐다. 이제서야 각각의 연동하는 방식을 이해할 수 있게 되었다. 끝-</p>
<blockquote>
<p>참고 및 출처</p>
</blockquote>
<ol>
<li>[새싹 X 코딩온] 영등포 캠퍼스 6기 입문자도 가능한 웹 개발자 부트캠프 강의 교안</li>
<li><a href="https://ittrue.tistory.com/250">https://ittrue.tistory.com/250</a></li>
<li><a href="https://mysterlee.tistory.com/49">https://mysterlee.tistory.com/49</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Apache Tomcat 이란?]]></title>
            <link>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-21%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-Apache-Tomcat-%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-21%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-Apache-Tomcat-%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Fri, 04 Oct 2024 15:26:26 GMT</pubDate>
            <description><![CDATA[<p>스프링부트를 배우는 시간이 되었다. 스프링부트를 이용해서 프로젝트를 만들고 실행하는 과정에서 Apache Tomcat이라는 것이 등장하였다. 그것에 대해 정리하고 서버가 어떻게 실행되는지 알아보자.</p>
<h2 id="아파치-apache">아파치 (Apache)</h2>
<p>아파치는 아파치 소프트웨어 재단에서 만든 <strong>웹서버(web server)</strong>로 정적 리소스를 응답한다. (80포트)</p>
<blockquote>
<p><strong>Web Server?</strong>
http 프로토콜 기반으로 브라우저로부터의 요청을 서비스하는 기능을 담당하는 프로그램으로 <strong>정적인 데이터</strong>에 대한 처리를 담당한다.</p>
</blockquote>
<h2 id="톰캣-tomcat">톰캣 (Tomcat)</h2>
<p>웹 서버와 웹 컨테이너의 결합으로 <strong>WAS(웹 애플리케이션 서버)</strong>이다. 동적인 데이터를 처리한다. (8080포트) JSP와 서블릿을 처리한다.</p>
<blockquote>
<p><strong>WAS? (Web Application Server)</strong>
여러 웹 클라이언트의 요구를 웹서버가 감당할 수 없는 기능을 구조적으로 웹서버와 분리하기 위해 만들어진것이다. <strong>동적인 데이터</strong> 처리를 담당한다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/_u__me_with/post/4580cdca-af78-4648-b7b5-5e7f08f0b83b/image.png" alt=""></p>
<p><strong>+ AP 서버(애플리케이션 서버)</strong>
자바 웹 애플리케이션을 구동하기 위해 필요한 프로그램으로 <strong>Tomcat</strong>이 이에 해당한다.</p>
<h3 id="jsp-java-server-pages">JSP (Java Server Pages)</h3>
<p>HTML 코드에 Java 코드를 넣어 동적 웹 페이지를 생성하는 웹 애플리케이션 도구로 서블릿을 작성하지 않고도 간편하게 웹프로그래밍을 구현하게 만든 기술이다.
Java소스를 사용하여 웹페이지에 접근하고 페이지 전환을 통하여 웹브라우저에 전달한다.</p>
<h3 id="servlet-서블릿">Servlet 서블릿</h3>
<p><strong>요청을 수신하고 응답을 반환하는 프로그램</strong>을 만들기 위한 자바 표준 기술이다. 즉 <strong>동적 웹페이지</strong>를 만들때 사용한다.</p>
<p>&lt;특징&gt;</p>
<ul>
<li>Servlet 클래스를 AP 서버(애플리케이션 서버-톰캣)에 등록하면 <strong>HTTP 요청에 대응</strong>하여 AP서버가 Servlet 객체의 메서드를 호출</li>
<li>MVC 패턴에서 컨트롤러로 이용</li>
<li>웹 컨테이너에서 실행</li>
</ul>
<p>&lt;동작과정&gt;</p>
<ol>
<li>클라이언트 요청</li>
<li>서블릿 객체 생성</li>
<li>어느 서블릿에 대해 요청한 것인지 탐색 후 호출</li>
<li>동적 페이지 생성 후 응답 전송</li>
</ol>
<blockquote>
<p>JSP VS. Servlet
JSP: HTML 페이지 내에서 자바 코드를 삽입 (뷰 생성)
Servlet: HTML을 자바 코드 내에서 출력 (비즈니스 로직 처리)</p>
</blockquote>
<p>결론!!
스프링부트는 아파치톰캣이 내장되어있어 따로 설치가 불필요하다!~!</p>
<h2 id="마무리하며">마무리하며</h2>
<p>이정도 개념으로 이해하고 넘어가도 될 것같다. 결론만 기억하면 된다. 스프링부트는 애플리케이션 서버가 내장되어있어 따로 설치 및 설정을 안해도 쉽게 서버를 실행하고 배포할 수 있다는 것이다.</p>
<blockquote>
<p>출처 및 참고</p>
</blockquote>
<ol>
<li>[새싹 X 코딩온] 영등포 캠퍼스 6기 입문자도 가능한 웹 개발자 부트캠프 강의 교안</li>
<li><a href="https://inpa.tistory.com/entry/TOMCAT-%E2%9A%99%EF%B8%8F-%EC%84%A4%EC%B9%98-%EC%84%A4%EC%A0%95-%EC%A0%95%EB%A6%AC">https://inpa.tistory.com/entry/TOMCAT-%E2%9A%99%EF%B8%8F-%EC%84%A4%EC%B9%98-%EC%84%A4%EC%A0%95-%EC%A0%95%EB%A6%AC</a></li>
<li><a href="https://velog.io/@falling_star3/Tomcat-%EC%84%9C%EB%B8%94%EB%A6%BFServlet%EC%9D%B4%EB%9E%80">https://velog.io/@falling_star3/Tomcat-%EC%84%9C%EB%B8%94%EB%A6%BFServlet%EC%9D%B4%EB%9E%80</a></li>
<li><a href="https://velog.io/@remon/%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EB%B3%B8-%EC%A7%80%EC%8B%9D-WEB%EC%95%84%ED%8C%8C%EC%B9%98%EA%B3%BC-WAS%ED%86%B0%EC%BA%A3-%EC%B0%A8%EC%9D%B4">https://velog.io/@remon/%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EB%B3%B8-%EC%A7%80%EC%8B%9D-WEB%EC%95%84%ED%8C%8C%EC%B9%98%EA%B3%BC-WAS%ED%86%B0%EC%BA%A3-%EC%B0%A8%EC%9D%B4</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java main 메소드가 무엇이지?]]></title>
            <link>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-20%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-Java-main-%EB%A9%94%EC%86%8C%EB%93%9C%EA%B0%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EC%A7%80</link>
            <guid>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-20%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-Java-main-%EB%A9%94%EC%86%8C%EB%93%9C%EA%B0%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EC%A7%80</guid>
            <pubDate>Sat, 28 Sep 2024 06:56:43 GMT</pubDate>
            <description><![CDATA[<p>자바 수업을 들으면서 항상 main 메소드를 만들고 시작한다. 근데 main 메소드가 뭐지?
계속 헷갈려서 한 번 정리해보았다.</p>
<h2 id="main-메소드란">main 메소드란?</h2>
<blockquote>
<p>가장 먼저 실행되는 메소드로 자바 프로그램의 <strong>&quot;시작점&quot;</strong>이다.</p>
</blockquote>
<p>자바 프로그램이 시작될 때 JVM은 메인 클래스를 먼저 찾고 그 클래스의 main 메소드를 실행한다.</p>
<p>그렇다면 main 메소드의 코드를 분석해보자!</p>
<pre><code class="language-java">public class Main {
    // main 메소드
    public static void main(String[] args) {

    }
}</code></pre>
<h2 id="main-메소드-코드-분석">main 메소드 코드 분석</h2>
<p>다음과 같이 5가지로 나눠볼 수 있다.</p>
<blockquote>
<p><em>출처) <a href="https://velog.io/@gyoung9707/Java-main-%EB%A9%94%EC%84%9C%EB%93%9C">https://velog.io/@gyoung9707/Java-main-%EB%A9%94%EC%84%9C%EB%93%9C</a></em></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/_u__me_with/post/8d7f3b47-9d80-4bd4-bd10-ebc5f08ea610/image.png" alt=""></p>
<h3 id="접근제어자---public">접근제어자 - public</h3>
<ul>
<li>외부에서 <strong>접근</strong>할 수 있는 일종의 <strong>제약</strong>의 역할</li>
<li>여기에서 public은 모든 클래스에서 접근이 가능하다는것을 의미</li>
<li><strong>public을 사용한 이유</strong>: 모든 실행 프로그램의 <strong>기본</strong>이 되는 함수로 <strong>어디에서나 접근이 가능</strong>해야하기 때문이다.</li>
<li><blockquote>
<p>JVM이 접근!</p>
</blockquote>
</li>
</ul>
<h3 id="정적-함수---static">정적 함수 - static</h3>
<ul>
<li>클래스가 생성되는 순간에 메모리를 할당 받음</li>
<li>즉, <strong>객체를 생성하지 않아도 static 멤버에 접근이 가능</strong></li>
<li><strong>static를 사용한 이유</strong>: <strong>객체가 생성되지 않은 상태</strong>에서도 <strong>메소드를 호출</strong>할 수 있어야하기 때문이다.</li>
</ul>
<h3 id="리턴타입---void">리턴타입 - void</h3>
<ul>
<li>메소드가 끝날 때 <strong>리턴값이 없음</strong>을 의미</li>
<li>main 메소드는 프로그램의 실행을 시작하고 모든 동작을 마친 후 JVM으로 돌아가는데 이때 실행 후 <strong>반환해야 할 값이 없음</strong>.</li>
<li><strong>void를 사용한 이유</strong>: JVM이 프로그램을 시작하고 끝낼 때 별도의 값을 기대하지않기 때문</li>
</ul>
<h3 id="메소드명---main">메소드명 - main</h3>
<ul>
<li><strong>변경 불가능!</strong></li>
<li>JVM가 진입점으로 인식하지 못해 오류 발생</li>
</ul>
<h3 id="매개변수---string-args">매개변수 - String[] args</h3>
<ul>
<li>args 문자열을 배열로 사용하겠다는 의미</li>
<li>프로그램을 실행할 때 특정한 정보를 입력 받아 그에 맞게 동작하게 하며 그것을 받는 공간</li>
<li>즉, 커맨드 라인 인자를 받기 위한 배열
참고) <a href="https://lordofkangs.tistory.com/12">https://lordofkangs.tistory.com/12</a></li>
</ul>
<h2 id="마무리하며">마무리하며</h2>
<p>main 메소드를 정리하며 접근제어자와 static 그리고 리턴타입에 대해서도 정리하게 되어 자바 수업을 더 이해할 수 있게 된 거같아 정리하길 잘했다라는 생각이 든다. 자바 수업도 열심히 들어서 백엔드와 소통이 더 잘 되는 프론트엔드가 되자!</p>
<blockquote>
<p>출처 및 참고</p>
</blockquote>
<ol>
<li>[새싹 X 코딩온] 영등포 캠퍼스 6기 입문자도 가능한 웹 개발자 부트캠프 강의 교안</li>
<li><a href="https://devparker.tistory.com/10">https://devparker.tistory.com/10</a></li>
<li><a href="https://velog.io/@gyoung9707/Java-main-%EB%A9%94%EC%84%9C%EB%93%9C">https://velog.io/@gyoung9707/Java-main-%EB%A9%94%EC%84%9C%EB%93%9C</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[팀 프로젝트 회고록 | Smile Hub]]></title>
            <link>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-2%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0-Smile-Hub</link>
            <guid>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-2%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0-Smile-Hub</guid>
            <pubDate>Mon, 16 Sep 2024 09:52:32 GMT</pubDate>
            <description><![CDATA[<p>교육과정 2차 프로젝트가 어느덧 끝이 났다. 언제 끝나나했는데 열심히 하다보니 끝나는 날이 오긴한다. 내가 담당했던 역할에 대해 간략히 정리해보고 4L 회고를 바탕으로 회고를 작성해볼려고한다.
여기서 4L 회고란?</p>
<ul>
<li>Liked : 좋았던 점은 무엇인가?</li>
<li>Lacked : 아쉬웠던 점, 부족한 점은 무엇인가?</li>
<li>Learned : 배운 점은 무엇인가?</li>
<li>Longed for : 앞으로 바라는 것은 무엇인가?</li>
</ul>
<h2 id="1-프로젝트-소개">1. 프로젝트 소개</h2>
<p><strong>Smile Hub</strong></p>
<ul>
<li>주제: 새상품 정보를 알 수 있는 중고거래 사이트</li>
<li>주제 선정 이유: 중고상품 구매 시 새상품 가격 비교를 위한 다른 사이트 방문의 번거러움을 줄이고자 함.</li>
</ul>
<p><strong>진행기간</strong>
2024.08.26 ~ 09.13 (3주)</p>
<p><strong>팀 구성</strong>
백엔드: 2명 / 프론트엔드: 2명</p>
<p><strong>기술스택</strong></p>
<ul>
<li>백엔드: node.js, express, npm, sequelize, multer, mysql</li>
<li>프론트엔드: <strong>JavaScript, React, Redux-Toolkit, JWT, Axios, Tailwind-css</strong></li>
<li>서버: AWS, Nginx, EC2, RDS, S3</li>
<li>소통: Git, GitHubm Pigma</li>
</ul>
<h2 id="2-내가-맡은-역할">2. 내가 맡은 역할</h2>
<p><strong>포지션</strong>
프론트엔드</p>
<p><strong>구현 기능</strong></p>
<ol>
<li>로그인 / 회원가입 (카카오 주소 API)</li>
<li>마이페이지 (프로필 업로드, 찜/구매/판매 내역, 배송현황)</li>
</ol>
<h2 id="3-회고">3. 회고</h2>
<h3 id="liked">Liked</h3>
<ul>
<li><strong>책임감</strong>
이번 프로젝트에서는 새벽까지 작업을 해야 하는 날이 많았다. 팀 내 문제로 인해 시간이 부족해졌고 원래 맡았던 것보다 많은 부분을 해결해야했다. 그럼에도 시간을 더 투자하여 끝까지 해냈다. 만약이 책임감이 없었더라면 완성하지 못했을 것이다.</li>
<li><strong>AI의 도움을 최대한 안 받을려고 한 노력</strong>
지난 교육 중간평가에서 검색을 통해 구현하는 능력이 많이 부족하다는 걸 느꼈다. 그 후로 학습 과정에서 최대한 AI의 도움없이 문제를 해결하려고 노력했다. 이번 2차 프로젝트에서도 그런 노력 자체를 했다는 점에서 잘했다고 말하고싶다.</li>
</ul>
<h3 id="lacked">Lacked</h3>
<ul>
<li><strong>많은 기능을 보여줄려고 했던 욕심</strong>
기업에 팀의 결과물로 많은 기능을 구현했다는 점을 어필해야 한다는 압박을 느꼈던 것 같다. 설계 단계에서 너무 많은 기능을 기획한 점이 아쉬웠고 부족한 점이다.</li>
<li><strong>팀원끼리의 소통</strong>
백엔드와 프론트엔드의 소통, 프론트엔드끼리의 소통 모두 아쉬웠다.<ol>
<li>백엔드와 프론트엔드
API 명세서를 먼저 정리하지 않은 탓에 연결 과정이 오래 걸렸다.</li>
<li>프론트엔드
남은 작업량에 대해 제대로 공유하지 않고, &#39;잘하고 있겠지&#39;라는 생각에 소통이 부족했다.
이 경험을 통해 객관적으로 일정을 공유하고 공용 문서를 정리하는 도구들의 필요성을 느꼈다. 앞으로는 이러한 도구들을 적극 활용해야겠다.</li>
</ol>
</li>
</ul>
<h3 id="learned">Learned</h3>
<ul>
<li><strong>덜어낼 줄 알아야한다</strong>
많은 기능을 보여줘야 한다는 욕심과 압박때문에 기능을 덜어낼 적절한 타이밍을 놓쳤다. 이번 2차 프로젝트를 통해 어필할 기능과 포기할 기능을 구분하는 법을 배웠다.</li>
<li><strong>React의 확장성</strong>
수업때 배운 개념으로말고도 프로젝트를 진행하면서 React의 다양한 확장성을 알게 되었다. 앞으로 다양한 프로젝트를 통해 계속 배워나가야겠다는 것을 깨달았다.</li>
</ul>
<h3 id="longed-for">Longed for</h3>
<ul>
<li>다양한 사이트를 많이 보는것
1차 프로젝트때도 느꼈지만좋은 사이트뿐만 아니라 안 좋은 사이트도 많이 보며 디자인과 개발 감각을 익히는 것이 중요하다는 것을 깨달았다. 앞으로 이런 부분을 더 열심히 노력해야겠다고 생각했다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Redux Persist 사용하기]]></title>
            <link>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-18%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-Redux-Persist-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-18%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-Redux-Persist-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 15 Sep 2024 13:58:21 GMT</pubDate>
            <description><![CDATA[<p>프로젝트를 진행하던 중 회원정보가 전역으로 사용되어야해서 Redux로 상태를 관리하였다. 하지만 새로고침하면 저장되었던 정보가 초기화 된다? 이게 뭐지싶었다.
초기화되는 이유를 알아보고 해결방법 중 한가지인 Redux Persist에 대해 알아보자.</p>
<h2 id="redux-상태가-새로고침시-초기화-되는-이유">Redux 상태가 새로고침시 초기화 되는 이유</h2>
<p>리덕스는 메모리 내에서 상태를 관리하게 된다. 그래서 새로고침을 하면 브라우저의 메모리가 초기화되기 때문에 리덕스 스토어의 상태도 사라지는 것이다. 따라서 리덕스 미들웨어를 사용하거나 Redux Persist 라이브러리를 활용할 수 있다. 이번에는 Redux Persist을 사용해보았다.</p>
<h2 id="redux-persist">Redux Persist</h2>
<p>리덕스 상태를 지속적으로 저장하고 복원하기 위한 라이브러리이다. 로컬 스토리지나 세션 스토리지에 저장하여 페이지가 새로고침 되어도 상태를 유지할 수 있게 해준다.</p>
<h3 id="설치">설치</h3>
<p><code>npm install redux-persist</code></p>
<h3 id="개념">개념</h3>
<ol>
<li><strong>persistReducer</strong>: 상태를 로컬 스토리지에 저장할 수 있도록 rootReducer를 감싸는 역할</li>
<li><strong>persistStore</strong>: 상태를 로컬 스토리지에서 불러오고 저장소를 생성</li>
<li><strong>persistGate</strong>: 상태를 복원할 때까지 대기하는 동안 로딩 컴포넌트를 표시</li>
</ol>
<pre><code class="language-javascript">// 기본 설정
import { createStore } from &#39;redux&#39;;
import { persistStore, persistReducer } from &#39;redux-persist&#39;;
import storage from &#39;redux-persist/lib/storage&#39;; // 기본 로컬 스토리지 사용
import rootReducer from &#39;./reducers&#39;;

const persistConfig = {
  key: &#39;root&#39;,
  storage,
};

const persistedReducer = persistReducer(persistConfig, rootReducer);

const store = createStore(persistedReducer);
const persistor = persistStore(store);

// 스토어 사용
import { PersistGate } from &#39;redux-persist/integration/react&#39;;

function App() {
  return (
    &lt;PersistGate loading={null} persistor={persistor}&gt;
      &lt;YourComponent /&gt;
    &lt;/PersistGate&gt;
  );
}</code></pre>
<h2 id="문제-상황-1">문제 상황 1</h2>
<p>로그인할 때 상태를 로컬 스토리지에 저장하였다. 근데 이메일, 비밀번호를 잘못 입력시에 보여주는 에러메시지는 새로고침하면 사라져야한다. (에러메시지도 기본 상태에서 관리되는 필드 - error)</p>
<h3 id="해결">해결</h3>
<pre><code class="language-javascript">const persistConfig = {
  key: &#39;root&#39;,
  storage,
  blacklist: [&#39;error&#39;], // &#39;error&#39; 상태를 persist하지 않도록 설정
};</code></pre>
<ul>
<li><strong>blacklist</strong>: 특정 리덕스 스토어의 상태를 로컬 스토리지에 저장하지 않도록 설정하는 기능.
즉, 새로고침 시 저장되지않고 <strong>항상 초기 상태</strong>로 돌아간다.</li>
<li>활용 사례: 사용자 입력을 받는 폼의 상태처럼 임시로 필요한 데이터는 저장할 필요가 없을 때, 특정 상태가 항상 초기 상태로 시작해야하는 경우
따라서 error 상태는 이메일이나 비밀번호가 틀렸을 경우에만 보여줘야하므로 저장할 필요가 없어서 blacklist 기능을 사용하였다.</li>
</ul>
<h2 id="문제-상황2">문제 상황2</h2>
<p>로컬 스토리지에 저장된 isAuthentication(권한의 유뮤를 boolean 값으로 표현)가 중복되어 출력하는 문제 즉, boolean 값이 로컬 스토리지에 문자열로 저장되는 문제이다.
정리하자면,</p>
<ol>
<li>로컬 스토리지의 저장 방식: 로컬 스토리지는 <strong>모든 데이터를 문자열</strong>로 저장</li>
<li>상태 복원 방법: Redux Persist는 상태를 복원할 때 문자열로 저장된 그대로 사용하게 되어 타입이 일치하지 않는 문제가 발생하여 중복 출력이 발생할 수 있다.</li>
</ol>
<h3 id="해결-1">해결</h3>
<p>stateReconciler를 사용하여 inboundState에서 복원된 상태를 처리하고 isAuthenticated의 값을 boolean 타입으로 변환한다.</p>
<pre><code class="language-javascript">
const persistConfig = {
  key: &#39;root&#39;,
  storage,
  blacklist: [&#39;error&#39;], // &#39;error&#39; 상태를 persist하지 않도록 설정

  // 사용자 데이터를 다시 불러올 때 boolean으로 변환
  stateReconciler: (inboundState, originalState, reducedState) =&gt; {
    // inboundState: 로컬 스토리지에서 복원된 상태
    // originalState: Redux스토어의 현재 상태
    // reducedState: 리듀서에서 반환된 상태

    if (inboundState &amp;&amp; inboundState.currentUser) {
      // isAuthenticated 값이 string이면 boolean으로 변환
      inboundState.currentUser.isAuthenticated = 
        inboundState.currentUser.isAuthenticated === &#39;true&#39; ? true : false;
    }
    return reducedState;
  },
};</code></pre>
<h2 id="마무리하며">마무리하며</h2>
<p>Redux의 사용법에 대해 한가지 더 알게 되었다. 전역으로 상태를 관리하지만 메모리에 저장되어 새로고침시 그 상태가 초기화된다. 이 상태를 로컬 스토리지나 세션 스토리지에 저장해야지만 그 상태값을 사용할 수 있다. 이번에는 Redux Pesiste 방법을 사용해봤지만 더 공부해서 다음에는 다른 방법도 적용해보아야겠다.</p>
<blockquote>
<p>출처 및 참고</p>
</blockquote>
<ol>
<li><a href="https://www.npmjs.com/package/redux-persist">https://www.npmjs.com/package/redux-persist</a></li>
<li><a href="https://velog.io/@bcl0206/%EC%83%88%EB%A1%9C%EA%B3%A0%EC%B9%A8-%ED%9B%84%EC%97%90%EB%8F%84-store-state-%EC%9C%A0%EC%A7%80%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95-Redux-persist">https://velog.io/@bcl0206/%EC%83%88%EB%A1%9C%EA%B3%A0%EC%B9%A8-%ED%9B%84%EC%97%90%EB%8F%84-store-state-%EC%9C%A0%EC%A7%80%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95-Redux-persist</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[트러블 슈팅) React Hook Form과 Kakao 주소 API를 활용한 Redux 상태 관리 - setVaule 속성]]></title>
            <link>https://velog.io/@_u__me_with/%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-React-Hook-Form%EC%9D%98-setValue-%ED%95%A8%EC%88%98%EC%9D%98-%EC%97%AD%ED%95%A0</link>
            <guid>https://velog.io/@_u__me_with/%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-React-Hook-Form%EC%9D%98-setValue-%ED%95%A8%EC%88%98%EC%9D%98-%EC%97%AD%ED%95%A0</guid>
            <pubDate>Fri, 30 Aug 2024 16:37:25 GMT</pubDate>
            <description><![CDATA[<p><strong>React Hook Form</strong>으로 회원가입 폼을 구현하던 중, <strong>Kakao 주소 API와 연동</strong>하는 과정에서 <strong>Redux Toolkit 상태가 정상적으로 업데이트되지 않는 문제</strong>가 발생하였다. 이 글에서 문제 상황, 원인, 해결 방법을 공유하고자한다.</p>
<blockquote>
<p>여기서 잠깐!
<strong>React Hook Form</strong>이 뭘까?</p>
</blockquote>
<p>React Hook Form은 React 애플리케이션에서 폼을 간단하고 효율적으로 관리할 수 있도록 돕는 라이브러리이다.</p>
<ul>
<li><strong>가볍고 빠른 성능</strong>: 불필요한 리렌더링을 줄여 성능을 최적화한다.</li>
<li><strong>편리한 검증 기능</strong>: 내장된 유효성 검사를 통해 간편하게 검증 기능을 사용할 수 있다.</li>
</ul>
<h2 id="문제-상황">문제 상황</h2>
<p>React 프로젝트에서 회원가입 폼의 입력 항목을 <strong>React Hook Form</strong>을 사용하여 관리하였다.
그 중에서 주소 입력은 <strong>Kakao 주소 API</strong>를 통해 사용자로부터 값을 받아와 <strong>Redux Toolkit으로 상태를 관리</strong>하였다.</p>
<p><strong>문제 상황</strong></p>
<ul>
<li>Input 필드로 입력한 값은 Redux 상태에 정상적으로 업데이트</li>
<li><strong>Kakao 주소 API에서 받은 값은 <code>onSubmit</code> 함수에서 제출된 <code>data</code> 객체에 반영되지 않음</strong>
즉, 카카오 API 로 입력된 값이 폼 데이터로 저장되지 않고 제출 시 빈 값으로 전달
<img src="https://velog.velcdn.com/images/_u__me_with/post/acb7b81b-5fa7-4dbd-9392-ce7175cb245a/image.png" alt=""></li>
</ul>
<h2 id="문제-원인">문제 원인</h2>
<p>문제를 분석한 결과, React Hook Form의 <code>setValue</code> 메서드를 사용하지 않은 점이 원인이었다.</p>
<ol>
<li>Kakao 주소 API에서 반환된 데이터를 React Hook Form과 연동하지 않았음</li>
<li>React Hook Form은 해당 필드의 값이 업데이트되었다는 정보를 알지 못해 상태 관리에 문제가 발생</li>
</ol>
<h2 id="문제-해결-방법">문제 해결 방법</h2>
<h3 id="1-redux-상태-로직-검증">1. Redux 상태 로직 검증</h3>
<p>Redux의 상태가 업데이트 되지않아 가장 먼저 Redux 상태 관리 로직을 살펴보았다. Redux의 <code>reducer</code>가 해당 데이터를 업데이트하는 로직이 제대로 동작하는지 확인하였다.
<img src="https://velog.velcdn.com/images/_u__me_with/post/948576c0-0b32-4693-be0c-c48cd84964a2/image.png" alt=""></p>
<pre><code class="language-javascript">setUserField: (state, action) =&gt; {
      const { field, value } = action.payload;
      console.log(&#39;field &gt;&gt;&#39;, field);
      console.log(&#39;value &gt;&gt;&#39;, value);

      // &#39;address.&#39;로 시작하는 필드인지 확인
      if (field.startsWith(&#39;address.&#39;)) {
        // 구조 분해 할당을 사용하여 addressField 추출
        const [, addressField] = field.split(&#39;.&#39;);
        console.log(&#39;addressField&#39;, addressField);

        // 구조 분해 할당을 사용하여 address 필드 업데이트
        state.currentUser.address = {
          ...state.currentUser.address,
          [addressField]: value,
        };</code></pre>
<p>정상적으로 업데이트되는 것을 확인하여 원인이 <strong>Redux 로직이 아니라 React Hook Form과의 연동 문제</strong>을 파악하였다.</p>
<h3 id="2-kakao-주소-api-데이터-흐름-분석">2. Kakao 주소 API 데이터 흐름 분석</h3>
<p>Kakao 주소 API에서 반환된 데이터가 Redux 상태에 전달되지 않는 이유를 찾기 위해 데이터 흐름을 추적하였다.
카카오 주소 api에 나와있는 반환값처럼 출력되고있다.</p>
<blockquote>
<p><a href="https://postcode.map.daum.net/guide">https://postcode.map.daum.net/guide</a></p>
</blockquote>
<p>즉, 주소 데이터 자체를 제대로 전달되고 있었다.</p>
<h3 id="3-redux-devtools로-상태-확인">3. Redux DevTools로 상태 확인</h3>
<p><img src="https://velog.velcdn.com/images/_u__me_with/post/957d7929-3b0b-4fcb-b0a9-e6b24765011a/image.png" alt="">
Redux 상태 자체에는 문제가 없고 React Hook Form과 Redux 상태 간 동기화 문제인것을 알게 되었다.</p>
<h3 id="4-react-hook-form의-데이터-관리-방식-조사">4. React Hook Form의 데이터 관리 방식 조사</h3>
<p>React Hook Form 공식 문서와 검색을 통해 확인한 결과, </p>
<ul>
<li>React Hook Form은 기본적으로 입력 필드 값만 내부적으로 관리한다.</li>
<li>외부 데이터를 React Hook Form의 필드 값으로 설정하려면 <code>setVaule</code> 메서드를 사용해야 한다는 것을 발견하였다.</li>
</ul>
<pre><code class="language-javascript">// useForm 설정
  const {
    register,
    handleSubmit,
    watch,
    formState: { errors },
    reset, // 폼 리셋을 위한 기능 추가
    setValue,
  } = useForm({
    mode: &#39;onChange&#39;,
    defaultValues: {
      gender: &#39;default&#39;,
    },
  });

    // react-hook-form 상태 업데이트
    setValue(&#39;address.postcode&#39;, data.zonecode);
    setValue(&#39;address.address&#39;, addr);
    setValue(&#39;address.extraAddress&#39;, extraAddr);</code></pre>
<p><strong>setValue 속성이 뭐지?</strong>
등록된 필드 의 값을 동적으로 설정 하고 폼 상태를 검증하고 업데이트할 수 있는 옵션</p>
<blockquote>
<p><a href="https://react-hook-form.com/docs/useform/setvalue">https://react-hook-form.com/docs/useform/setvalue</a></p>
</blockquote>
<h2 id="해결-후-배운점">해결 후 배운점</h2>
<p><strong>1. React Hook Form 과 외부 데이터 연동 방법</strong>
React Hook Form에서 외부 데이터를 폼 필드에 반영하려면 <code>setValue</code> 메서드가 필요하다는 점을 배웠다. 사용할 기술이나 라이브러리 등의 문서를 참고하며 개발해야겠다고 생각하였다.
<strong>2. 디버깅 도구의 활용</strong>
Redux DevTools를 사용해 Redux 상태를 확인하며 문제를 단계적으로 좁혀가는 디버깅의 중요성을 깨달았다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[
Redux Toolkit state 에러 (reducer 중첩 사용 - key 중복 X)]]></title>
            <link>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-16%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-redex-toolkit-state-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-16%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-redex-toolkit-state-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Fri, 30 Aug 2024 13:08:39 GMT</pubDate>
            <description><![CDATA[<p>Redux 사용 중 state 에러 발생이 나타났다. 그 이유와 해결 방법에 대해 정리해보자.</p>
<h2 id="문제-상황">문제 상황</h2>
<p><img src="https://velog.velcdn.com/images/_u__me_with/post/3c256657-3421-4c14-8515-5d55e727bc40/image.png" alt="">
Redux와 <code>useSelector</code> Hook을 사용하여 상태를 가져오려고 하였으나 다음과 같이 undefined라는 에러가 발생하였다.</p>
<h2 id="문제-원인">문제 원인</h2>
<p><img src="https://velog.velcdn.com/images/_u__me_with/post/eab3e807-4c71-42ba-9533-abeba8059afe/image.png" alt=""></p>
<p>Redux Store의 설정 과정에서 reducer를 중접한 구조로 설정했기 때문이다.
<code>configureStore</code>에서 <code>reducer</code>라는 키 값에 이미 <code>rootReducer</code>를 할당했음에도 이를 다시 중첩된 <code>reducer</code> 키로 정의했기 때문에 최종 상태 구조가 불필요하게 깊어졌다.</p>
<p>잘못된 Store 설정 코드:</p>
<pre><code class="language-javascript">// store 설정
import { configureStore } from &#39;@reduxjs/toolkit&#39;;
import rootReducer from &#39;./rootReducer&#39;;

const rootStore = configureStore({
  reducer: {
    reducer: rootReducer, // &#39;reducer&#39; 키에 다시 reducer를 중첩함
  },
});

export default rootStore;</code></pre>
<p>이로 인해 <code>useSelector</code>로 상태를 가져오려고 할때 중첩된 상태 구조로 인해 올바른 경로를 찾지 못하고 <code>undefined</code>를 반환하여 에러를 발생시켰다.</p>
<h2 id="문제-해결">문제 해결</h2>
<p><strong>1. Store 설정 수정</strong>
<code>configureStore</code> 의 <code>reducer</code> 키에 <code>rootReducer</code> 를 직접 할당하도록 수정하여 중첩 구조를 제거</p>
<pre><code class="language-javascript">// store 설정
import { configureStore } from &#39;@reduxjs/toolkit&#39;;
import rootReducer from &#39;./rootReducer&#39;;

const rootStore = configureStore({
  reducer: rootReducer,
});

export default rootStore;</code></pre>
<p><strong>2. 상태 접근 방식 확인</strong>
수정 후 로그로 상태 구조를 확인한 결과 올바르게 변경되었음을 확인
<img src="https://velog.velcdn.com/images/_u__me_with/post/7da39b2d-c3f8-49ab-a00f-04a51d4921e7/image.png" alt=""></p>
<h2 id="배운점-및-느낀점">배운점 및 느낀점</h2>
<p><strong>Key는 중복되어서는 안된다.</strong>
<code>configureStore</code> 에서 <code>reducer</code> 속성을 지정할 때, reducer라는 이름으로 중복된 키를 사용하면 Redux에서 이를 처리하는 데 혼란을 줄 수 있다. Redux는 각 리듀서를 고유한 키로 식별하기 때문이다.
만약 다중 리듀서를 사용해야할 경우</p>
<pre><code class="language-javascript">const rootStore = configureStore({
  reducer: {
    user: userReducer,
    posts: postsReducer,
  },
});</code></pre>
<p>이처럼 user, posts라는 key의 이름을 다르게 지정하면 된다.</p>
<p>이 경험을 통해 상태 관리의 한 가지 개념을 배우게 되었고 구현할 때 구조를 명확히 하고 코드의 흐름을 항상 점검하면서 개발을 해야겠다고 생각하는 계기되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[useReducer를 Redux로 리팩토링하기]]></title>
            <link>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-15%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-useReducer%EB%A5%BC-Redux%EB%A1%9C-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-15%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-useReducer%EB%A5%BC-Redux%EB%A1%9C-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 20 Aug 2024 11:46:16 GMT</pubDate>
            <description><![CDATA[<p>JS 상태관리 라이브러리인 Redux를 배웠다. 리액트의 상태관리 라이브러리로 가장 많이 사용하는데 컴포넌트 수가 많은 프로젝트에서 전역으로 상태를 관리하기 위해 사용한다.
간단히 용어 정리 후 useReducer hook으로 작성했던 코드를 Redux를 이용한 코드로 리팩토링해보겠다.</p>
<h2 id="redux-용어-정리">Redux 용어 정리</h2>
<ul>
<li><strong>Store</strong>: <strong>상태를 저장하는 오직 하나</strong>의 공간으로 Store 안에는 현재 애플리케이션 상태와 리듀서가 들어있다. 이 리듀서를 이용하여 데이터를 조작한다.</li>
<li><strong>Selector</strong>: 사용할 상태를 선택</li>
<li><strong>Action</strong>: 어떠한 <strong>상태의 변화</strong>를 일으키는 사건으로 컴포넌트에서 Store로 운반할 데이터이다. (객체 형태)</li>
<li><strong>Dispatch</strong>: <strong>Action을 Store랑 연결</strong>해주는 역할로 액션을 Store에 보내는 행위를 의미한다. 객체를 파라미터로 넣어서 호출.</li>
<li><strong>Reducer</strong>: <strong>새로운 상태를 반환</strong>하는 함수로 type에 따라 변화를 일으킨다.
👉 Action -&gt; Store -&gt; Reducer -&gt; 새로운 상태 생성 의 흐름으로 이해하면 된다.</li>
</ul>
<p>📌 시각적으로 이해하기
<img src="https://velog.velcdn.com/images/_u__me_with/post/c9352b91-2b4d-4b49-b4d0-051a3092ab2e/image.gif" alt=""></p>
<p>여기서 Redux Toolkit을 알아야 Redux를 더 편리하게 사용할 수 있는데 Redux Toolkit에 대해서도 간단히 정리해보자.</p>
<h2 id="redux-toolkitrtk">Redux Toolkit(RTK)</h2>
<p>일반적인 작업들을 단순화해주는 유틸리티가 포함되어있어 코드를 더 간결하게 작성할 수 있다.</p>
<ul>
<li><strong>configureStore()</strong>: <strong>Redux store를 생성</strong>하기 위한 함수, 여러 미들웨어와 Reducer를 쉽게 통합할 수 있다.</li>
<li><strong>createSlice()</strong>: <strong>Reducer와 Acion을 함께 생성</strong>하는 함수, 슬라이스라는 개념을 사용하여 액션 타입, 액션 생성 함수, 리듀서를 한 번에 정의한다.</li>
</ul>
<h2 id="usereducer-hook를-사용한-코드">useReducer hook를 사용한 코드</h2>
<pre><code class="language-javascript">(Bank.js)

import React, { useReducer, useState } from &quot;react&quot;;

// 초기값 설정
const initalState = { value: 0 };

// reducer 함수(type 정의)
const bankReducer = (state, action) =&gt; {

  switch (action.type) {
    case &quot;plus&quot;: {
      return { value: state.value + action.value };
    }
    case &quot;minus&quot;: {
      return { value: state.value - action.value };
    }
    default: {
      return { state };
    }
  }
};

function Bank() {
  // hook 사용
  const [money, setMoney] = useState(&quot;&quot;);
  const [state, dispatch] = useReducer(bankReducer, initalState);

  // 이벤트 핸들러 정의
  const onPlus = (e) =&gt; {
    dispatch({ type: &quot;plus&quot;, value: money });
    setMoney(&quot;&quot;);
  };
  const onMinus = (e) =&gt; {
    dispatch({ type: &quot;minus&quot;, value: money });
    setMoney(&quot;&quot;);
  };
  return (
    &lt;div&gt;
      &lt;h2&gt;짱구 은행&lt;/h2&gt;
      &lt;div&gt;잔고: {state.value}&lt;/div&gt;
      &lt;input
        type=&quot;number&quot;
        value={money}
        onChange={(e) =&gt; setMoney(parseInt(e.target.value))}
      /&gt;
      &lt;button onClick={onPlus}&gt;입금&lt;/button&gt;
      &lt;button onClick={onMinus}&gt;출금&lt;/button&gt;
    &lt;/div&gt;
  );
}

export default Bank;</code></pre>
<h2 id="redux로-리팩토링">Redux로 리팩토링</h2>
<p><strong>1. store/bankSlice.js</strong>
: createSlice함수를 사용하여 Reducer와 Action 생성자 정의</p>
<pre><code class="language-javascript">
import { createSlice } from &quot;@reduxjs/toolkit&quot;;

// 1. 슬라이스 객체 정의
const bankSlice = createSlice({
  name: &quot;bank&quot;,
  initialState: { number: 0 },
  reducers: {
    bankPlus: (state, action) =&gt; {
      state.number += action.payload;
    },
    bankMinus: (state, action) =&gt; {
      state.number -= action.payload;
    },
  },
});

export const { bankPlus, bankMinus } = bankSlice.actions;
export default bankSlice.reducer;</code></pre>
<p><strong>2. Bank.js</strong>
useSelector을 이용해 Store의 상태를 선택하고 useDispatch를 이용해 Action을 Store로 보냄.</p>
<pre><code class="language-javascript">import React, { useState } from &quot;react&quot;;
import { useDispatch, useSelector } from &quot;react-redux&quot;;
import { bankPlus, bankMinus } from &quot;../store/bankSlice&quot;;

export default function BankContainer() {
  // 사용할 상태 선택
  const bank = useSelector((state) =&gt; state.bank.number);

  const dispatch = useDispatch();

  const [money, setMoney] = useState(&quot;&quot;);

  // 이벤트 핸들러
  const onClickPlus = () =&gt; {
    dispatch(bankPlus(money));
    setMoney(&quot;&quot;);
  };

  const onClickMinus = () =&gt; {
    dispatch(bankMinus(money));
    setMoney(&quot;&quot;);
  };

  const onchangeMoney = (e) =&gt; {
    setMoney(Number(e.target.value));
  };

  return (
    &lt;div&gt;
      &lt;h3&gt;usereducer를 redux로 리팩토링&lt;/h3&gt;
      &lt;h2&gt;짱구 은행&lt;/h2&gt;
      &lt;h3&gt;잔고: {bank}&lt;/h3&gt;
      &lt;input type=&quot;text&quot; value={money} onChange={onchangeMoney} /&gt;
      &lt;button onClick={onClickPlus}&gt;입금&lt;/button&gt;
      &lt;button onClick={onClickMinus}&gt;출금&lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p><strong>3. store/index.js</strong>
configureStore 함수를 사용하여 Store 생성</p>
<pre><code class="language-javascript">// 2. store 생성
import { configureStore } from &quot;@reduxjs/toolkit&quot;;
import bankReducer from &quot;./bankSlice&quot;;

const store = configureStore({
  reducer: {
    bank: bankReducer,
  },
});

export default store;</code></pre>
<p><strong>4. index.js</strong>
redux를 사용하기 위해 provider를 사용하여 store 데이터를 전달</p>
<pre><code class="language-javascript">import React from &quot;react&quot;;
import ReactDOM from &quot;react-dom/client&quot;;
import App from &quot;./App&quot;;
import store from &quot;./store&quot;;
import { Provider } from &quot;react-redux&quot;;

const root = ReactDOM.createRoot(document.getElementById(&quot;root&quot;));
root.render(
  &lt;React.StrictMode&gt;
    {/* 3. store 연결 */}
    &lt;Provider store={store}&gt;
      &lt;App /&gt;
    &lt;/Provider&gt;
  &lt;/React.StrictMode&gt;
);</code></pre>
<h2 id="마무리하며">마무리하며</h2>
<p>위에 코드양으로만 보면 Redux가 복잡해보여 왜 사용하나할 수 있지만 컴포넌트가 많아지면 관리할 상태도 많아져 상태를 전역으로 관리할 수 있는 Redux가 효율적이다. 곧 있을 프로젝트에서 사용해야하는데 더 예시를 찾아보고 연습해야겠다.</p>
<blockquote>
<p>출처 및 참고</p>
</blockquote>
<ol>
<li>[새싹 X 코딩온] 영등포 캠퍼스 6기 입문자도 가능한 웹 개발자 부트캠프 강의 교안</li>
<li><a href="https://ko.redux.js.org/tutorials/essentials/part-1-overview-concepts">https://ko.redux.js.org/tutorials/essentials/part-1-overview-concepts</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[CORS란?]]></title>
            <link>https://velog.io/@_u__me_with/CORS%EB%9E%80-%EC%98%81%EB%93%B1%ED%8F%AC-%EC%BA%A0%ED%8D%BC%EC%8A%A4-6%EA%B8%B0-%EC%9E%85%EB%AC%B8%EC%9E%90%EB%8F%84-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%9B%B9-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-14%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A02</link>
            <guid>https://velog.io/@_u__me_with/CORS%EB%9E%80-%EC%98%81%EB%93%B1%ED%8F%AC-%EC%BA%A0%ED%8D%BC%EC%8A%A4-6%EA%B8%B0-%EC%9E%85%EB%AC%B8%EC%9E%90%EB%8F%84-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%9B%B9-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-14%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A02</guid>
            <pubDate>Sun, 18 Aug 2024 12:15:00 GMT</pubDate>
            <description><![CDATA[<p>독립적으로 동작하는 백엔드 애플리케이션과 프론트엔드 애플리케이션을 하나의 서비스로 통합!하는 과정을 진행하였다. 지금까지는 React만 이용하여 프론트엔드만 구현하였는데 node.js로 서비스 통합을 하였다.
그 과정에서 CORS란 개념을 배웠는데 이것에 대해 알아보자!</p>
<h2 id="corscross-origin-resource-sharing">CORS(Cross-Origin Resource Sharing)</h2>
<p>한글로 번역하면 <strong>&#39;교차 출처 리소스 공유&#39;</strong> 이다. 여기서 출처가 교차한다? 이건 무슨 뜻일까..? 결론부터 말하자면 리소스를 주고받으려는 <strong>두 출처가 서로 다르다</strong> 라는 뜻으로 CORS를 설정한다는건 <strong>&#39;출처가 다른 서버 간의 리소스 공유&#39;</strong> 를 허용한다는 것!
여기에서 의문은 <em>출처</em> 이다. 출처는 Origin을 번역한 것인데 이걸 알기전에 복습이 필요하다.</p>
<h3 id="url과-도메인domain">URL과 도메인(Domain)</h3>
<ul>
<li><strong>URL</strong> : Uniform Resource Locatior로 웹 주소이며 웹에서 리소스(HTML 페이지, CSS, 이미지 등)가 어디 있는지 알려주기 위한 주소.</li>
<li><strong>도메인</strong> : URL의 일부로 사람이 기억하기 어려운 IP 주소를 변환시켜주는 네임 서버. (이미지에는 hostname을 뜻함.)
<img src="https://velog.velcdn.com/images/_u__me_with/post/ec7777d6-7da8-4620-8454-cfc2fedfa2fc/image.png" alt=""></li>
</ul>
<h2 id="결론">결론</h2>
<p><img src="https://velog.velcdn.com/images/_u__me_with/post/5ea85fba-3028-406a-a9b4-eeef530cd984/image.png" alt="">
React(Frontend Server)는 포트 번호가 3000으로 요청하는데 node.js(Backend Server)는 포트 번호 8080을 사용중이므로 거절된다. 이 과정을 해결해주는 것이 CORS이다.
즉, <strong>서로 다른 Origin이라도 리소스 요청, 응답을 허락</strong>해주는 웹 보안 방침이다.</p>
<h2 id="마무리하며">마무리하며</h2>
<p>드디어 서버와 클라이언트를 연결하는 방법을 배우게 되었다. 혼자서 애플리케이션 하나를 만들 수 있는 개념을 배운거같아 즐거웠다. 더 공부해서 개발자가 되기까지 화이팅하자!
더 깊숙하게 알고 싶다면 아래에 링크를 참고하면 좋을거같다.</p>
<blockquote>
<p><a href="https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F">https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F</a></p>
</blockquote>
<blockquote>
<p>참고 및 출처</p>
</blockquote>
<ol>
<li><a href="https://docs.tosspayments.com/resources/glossary/cors">https://docs.tosspayments.com/resources/glossary/cors</a></li>
<li>[새싹 X 코딩온] 영등포 캠퍼스 6기 입문자도 가능한 웹 개발자 부트캠프 강의 교안</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[.map is not a function 에러 (mysql 권한 문제)]]></title>
            <link>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-14%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-.map-is-not-a-function-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-14%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-.map-is-not-a-function-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Fri, 16 Aug 2024 14:31:22 GMT</pubDate>
            <description><![CDATA[<p>react에서 map 함수 사용 중 만난 에러!
<code>todoItem.map is not a funcion</code>
map이 왜 함수가 아니야~~?!!
다양한 이유가 있을텐데 내가 경험한 이유에 대해 이야기 해보겠다.</p>
<h2 id="map-함수는-무엇인가">map 함수는 무엇인가</h2>
<p>일단 map 함수에 대해 알아야 왜 이런 에러가 발생하는지 알 수 있다.
map 함수는 <strong>배열을 순회</strong>해서 각 요소를 콜백 함수로 적용해서 처리해 모은 <strong>새로운 배열을 반환</strong>하기 위한 함수
즉!!! map 함수는 배열로 데이터를 가져오고 배열로 반환한다!!!</p>
<h2 id="에러-원인-파악">에러 원인 파악</h2>
<p><code>console.log(res.data)</code>를 통해 응답 데이터의 로그를 찍어봤는데 응답이 오지 않고 있었다.
그래서 <strong>DB와의 연결이 제대로 되고있지않다</strong>고 판단.!
user의 <strong>권한 정보를 확인</strong>해보았다.
모든 권한이 Y 즉, 모든 권한을 사용할 수 있어야한다.
하지만? user에 권한이 없어 데이터를 응답할 수 없었던 것이다.</p>
<h2 id="에러-해결">에러 해결</h2>
<p>user에게 모든 권한 주기!</p>
<pre><code class="language-sql">-- 현재 유저 확인
SELECT * FROM mysql.user;

-- 유저 생성
CREATE USER &#39;유저 이름&#39;@&#39;%&#39; IDENTIFIED BY &#39;비밀번호&#39;;

-- 생성한 유저에게 모든 권한 부여
GRANT ALL PRIVILEGES ON *.* TO &#39;유저 이름&#39;@&#39;%&#39; WITH GRANT OPTION;

-- 캐시 지움
FLUSH PRIVILEGES;
-- 현재 사용중인 MySql의 캐시를 지우고
-- 새로운 설정을 적용하기 위해서 사용되는 명령어
-- 사용자 권한 등을 변경하였을 때, MySQL에 변경사항을 적용하기 위해서 사용</code></pre>
<p>다음과 같이 유저에 권한을 부여하면 DB가 데이터를 제대로 보낸다.</p>
<h2 id="깨달은점">깨달은점</h2>
<p>에러가 났을땐 <strong>에러 메세지</strong>를 유심히 살펴보자.
분명히 해결할 힌트가 있을것!
그리고 가장 중요한 포인트는 <code>console.log(res.data)</code>와 같이 에러난 코드의 데이터 값을 <strong>로그로 찍어보기!</strong></p>
<p>이번 에러를 정리하자면</p>
<ol>
<li>map 함수가 함수가 아니라는 에러</li>
<li>map 함수의 특징을 생각 -&gt; 배열로 데이터를 받아옴.</li>
<li>로그를 찍어 더욱 자세한 에러 메세지 찾기</li>
</ol>
<p>끝으로 에러를 두려워하지말자! 😅</p>
<blockquote>
<p>출처 및 참고</p>
</blockquote>
<ol>
<li><a href="https://ygdev84.tistory.com/9">https://ygdev84.tistory.com/9</a></li>
<li>[새싹 X 코딩온] 영등포 캠퍼스 6기 입문자도 가능한 웹 개발자 부트캠프 강의 교안</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[useRef 여러개 관리하기]]></title>
            <link>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-13%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-useRef-%EC%97%AC%EB%9F%AC%EA%B0%9C-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-13%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-useRef-%EC%97%AC%EB%9F%AC%EA%B0%9C-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 08 Aug 2024 13:59:58 GMT</pubDate>
            <description><![CDATA[<p>useRef hook를 배웠다. 과제를 하던중 여러 input창에 모두 useRef를 이용하여 같은 기능을 구현하고싶은데 각각 따로따로 써야하나 의문이 들고 찾아본 결과를 정리해보겠다!</p>
<h2 id="useref란">useRef란?</h2>
<p>렌더링에 필요하지 않은 값을 참조할 수 있는 React Hook이다. 따라서 용도는 1. 정보 저장 2. DOM 조작 이다.</p>
<h2 id="구현할-기능">구현할 기능</h2>
<ul>
<li>input에 값이 입력되지 않으면 input창에 focus주기</li>
<li>input/textarea에서 조건에 충족하지 못하면 alert 띄우는 것은 UX 측면에서 좋지않다.</li>
<li>트렌디한 사이트는 alert을 사용하지않고 input에 focus 주는 방식을 사용</li>
<li>input창 3개에 적용할 것.</li>
</ul>
<h2 id="useref-각각-사용하기">useRef 각각 사용하기</h2>
<ol>
<li><p><strong>useRef 생성</strong></p>
<pre><code class="language-javascript">const inputWriterRef = useRef();
const inputTitleRef = useRef();
const inputSearchRef = useRef();</code></pre>
</li>
<li><p>직접 <strong>접근하고 싶은 DOM 요소</strong>에 ref props 설정</p>
<pre><code class="language-javascript">&lt;input ref = {inputWriterRef}/&gt;;
&lt;input ref = {inputTitleRef}/&gt;;
&lt;input ref = {inputSearchRef}/&gt;;</code></pre>
</li>
<li><p>useRef()를 이용해서 만든 객체의 current값에 focus() DOM API 사용</p>
<pre><code class="language-javascript">inputWriterRef.current.focus();
inputTitleRef.current.focus();
inputSearchRef.current.focus();</code></pre>
</li>
</ol>
<h3 id="📌-useref가-너무-많아지면-어떻게-사용하지">📌 useRef가 너무 많아지면 어떻게 사용하지?</h3>
<p>방법이 2가지가 있다.</p>
<ol>
<li>객체 사용
: <strong>명확한 이름으로 참조</strong>를 관리해야 하거나, 각 참조가 특정한 의미를 갖는 경우 유리</li>
<li>배열 사용
: <strong>동일한 유형의 여러 참조</strong>를 관리하거나, 참조가 순차적으로 추가되는 경우 유리(반복적인 UI 요소)</li>
</ol>
<p>이번 상황에서는 명확한 이름으로 관리하는게 적절하다 생각되어 객체를 사용하였다.</p>
<ol>
<li>useRef 객체 생성<pre><code class="language-javascript">const refs = useRef({
 writer: null,
 title: null,
 searchText: null,
});</code></pre>
</li>
<li>직접 접근하고 싶은 DOM 요소에 ref props 설정<pre><code class="language-javascript">ref={(el) =&gt; (refs.current.writer = el)}</code></pre>
</li>
</ol>
<ul>
<li>ref에 전달되는 콜백함수이다.</li>
<li>input 요소에 대한 DOM을 참조</li>
<li>refs.current.writer.focus()를 호출하여 포커스을 줌.</li>
</ul>
<ol start="3">
<li>useRef()를 이용해서 만든 객체의 current 값에 focus() DOM API 사용<pre><code class="language-javascript">if (writer.trim().length === 0) {
   refs.current.writer.focus();
   return;
 }</code></pre>
</li>
</ol>
<h3 id="전체-코드">전체 코드</h3>
<pre><code class="language-javascript">import React, { useRef, useState } from &quot;react&quot;;

function Practice() {
  const [text, setText] = useState([]);
  const [writer, setWriter] = useState(&quot;&quot;);
  const [title, setTitle] = useState(&quot;&quot;);
  const [searchOption, setSearchOption] = useState(&quot;writer&quot;);
  const [searchText, setSearchText] = useState(&quot;&quot;);
  const [filteredText, setFilteredText] = useState([]);

  // 객체를 사용하여 useRef 생성
  const refs = useRef({
    writer: null,
    title: null,
    searchText: null,
  });

  // 데이터 추가
  const addText = (e) =&gt; {
    e.preventDefault();
    const newText = text.concat({
      id: text.length + 1,
      writer: writer,
      title: title,
    });
    if (writer.trim().length === 0) {
      refs.current.writer.focus();
      return;
    }
    if (title.trim().length === 0) {
      refs.current.title.focus();
      return;
    }
    setText(newText);
    setWriter(&quot;&quot;);
    setTitle(&quot;&quot;);
  };

  // 검색 기능
  const handleSearch = () =&gt; {
    if (searchText.trim().length === 0) {
      //   setFilteredText([]);
      refs.current.searchText.focus();
      return;
    }

    const filtered = text.filter((info) =&gt; {
      if (searchOption === &quot;writer&quot;) {
        return info.writer.includes(searchText);
      } else if (searchOption === &quot;title&quot;) {
        return info.title.includes(searchText);
      }
      return false;
    });
    setFilteredText(filtered);
    setSearchText(&quot;&quot;);
  };

  // 전체 검색
  const handleSearchAll = () =&gt; {
    setFilteredText(text);
  };

  // 키보드 이벤트
  const handleKeyDown = (e, action) =&gt; {
    if (e.keyCode === 13) {
      action(e);
    }
  };

  return (
    &lt;&gt;
      &lt;form style={{ margin: &quot;10px&quot; }}&gt;
        작성자:
        &lt;input
          type=&quot;text&quot;
          placeholder=&quot;작성자&quot;
          value={writer}
          onChange={(e) =&gt; setWriter(e.target.value)}
          ref={(el) =&gt; (refs.current.writer = el)}
        /&gt;
        제목:
        &lt;input
          type=&quot;text&quot;
          placeholder=&quot;제목&quot;
          value={title}
          onChange={(e) =&gt; setTitle(e.target.value)}
          onKeyDown={(e) =&gt; handleKeyDown(e, addText)}
          ref={(el) =&gt; (refs.current.title = el)}
        /&gt;
        &lt;button onClick={addText}&gt;작성&lt;/button&gt; &lt;br /&gt;
      &lt;/form&gt;
      &lt;div style={{ margin: &quot;10px&quot; }}&gt;
        &lt;select
          value={searchOption}
          onChange={(e) =&gt; setSearchOption(e.target.value)}
        &gt;
          &lt;option value=&quot;writer&quot;&gt;작성자&lt;/option&gt;
          &lt;option value=&quot;title&quot;&gt;제목&lt;/option&gt;
        &lt;/select&gt;
        &lt;input
          type=&quot;text&quot;
          placeholder=&quot;검색어를 입력하세요.&quot;
          value={searchText}
          onChange={(e) =&gt; setSearchText(e.target.value)}
          onKeyDown={(e) =&gt; handleKeyDown(e, handleSearch)}
          ref={(el) =&gt; (refs.current.searchText = el)}
        /&gt;
        &lt;button onClick={handleSearch}&gt;검색&lt;/button&gt;
        &lt;button onClick={handleSearchAll}&gt;전체&lt;/button&gt;
      &lt;/div&gt;
      &lt;table
        border=&quot;1&quot;
        cellPadding=&quot;10&quot;
        cellSpacing=&quot;1&quot;
        style={{ margin: &quot;10px&quot; }}
      &gt;
        &lt;thead&gt;
          &lt;tr&gt;
            &lt;th&gt;번호&lt;/th&gt;
            &lt;th&gt;제목&lt;/th&gt;
            &lt;th&gt;작성자&lt;/th&gt;
          &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
          {text.map((value) =&gt; (
            &lt;tr key={value.id}&gt;
              &lt;td&gt;{value.id}&lt;/td&gt;
              &lt;td&gt;{value.title}&lt;/td&gt;
              &lt;td&gt;{value.writer}&lt;/td&gt;
            &lt;/tr&gt;
          ))}
        &lt;/tbody&gt;
      &lt;/table&gt;
      {filteredText.length &gt; 0 ? (
        &lt;p&gt;검색 결과&lt;/p&gt;
      ) : (
        &lt;p&gt;검색 결과가 없습니다.&lt;/p&gt;
      )}
      {filteredText.length &gt; 0 &amp;&amp; (
        &lt;table
          border=&quot;1&quot;
          cellPadding=&quot;10&quot;
          cellSpacing=&quot;1&quot;
          style={{ margin: &quot;10px&quot; }}
        &gt;
          &lt;thead&gt;
            &lt;tr&gt;
              &lt;th&gt;번호&lt;/th&gt;
              &lt;th&gt;제목&lt;/th&gt;
              &lt;th&gt;작성자&lt;/th&gt;
            &lt;/tr&gt;
          &lt;/thead&gt;
          &lt;tbody&gt;
            {filteredText.map((value) =&gt; (
              &lt;tr key={value.id}&gt;
                &lt;td&gt;{value.id}&lt;/td&gt;
                &lt;td&gt;{value.title}&lt;/td&gt;
                &lt;td&gt;{value.writer}&lt;/td&gt;
              &lt;/tr&gt;
            ))}
          &lt;/tbody&gt;
        &lt;/table&gt;
      )}
    &lt;/&gt;
  );
}

export default Practice;</code></pre>
<h2 id="마무리하며">마무리하며..</h2>
<p>리액트의 useRef hook에 대해 학습하였다. 용도를 알게 되었고 어떻게 하면 더 효율적으로 쓸 수 있을까 고민하는 시간이 되었다. 리액트에는 많은 hook이 존재한다고 하는데 차근히 배워보고 익혀야겠다.</p>
<blockquote>
<p>출처 및 참고</p>
</blockquote>
<ol>
<li>[새싹 X 코딩온] 영등포 캠퍼스 6기 입문자도 가능한 웹 개발자 부트캠프 강의 교안</li>
<li><a href="https://ko.react.dev/reference/react/useRef">https://ko.react.dev/reference/react/useRef</a></li>
<li><a href="https://velog.io/@minho100227/useRef%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-focus%EA%B8%B0%EB%8A%A5">https://velog.io/@minho100227/useRef%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-focus%EA%B8%B0%EB%8A%A5</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[React props.children란 무엇인가]]></title>
            <link>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-%EC%98%81%EB%93%B1%ED%8F%AC-%EC%BA%A0%ED%8D%BC%EC%8A%A4-6%EA%B8%B0-%EC%9E%85%EB%AC%B8%EC%9E%90%EB%8F%84-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%9B%B9-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-12%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-React-props</link>
            <guid>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-%EC%98%81%EB%93%B1%ED%8F%AC-%EC%BA%A0%ED%8D%BC%EC%8A%A4-6%EA%B8%B0-%EC%9E%85%EB%AC%B8%EC%9E%90%EB%8F%84-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%9B%B9-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-12%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-React-props</guid>
            <pubDate>Mon, 29 Jul 2024 14:48:43 GMT</pubDate>
            <description><![CDATA[<p>드디어 react 배우기 시작하였다.
리액트란? 화면을 만들기 위한 자바스크립트 라이브러리이다. 현재 프론트엔드 개발 환경에서 가장 많이 활용되고 있는 라이브러리이고 사용자와 상호 작용이 가능한 동적 UI 제작이 가능!!</p>
<p>그 중에서 props.children에 대해 알아보자🔥</p>
<h2 id="props">props</h2>
<p>properties 의 줄임말로 어떠한 값을 컴포넌트에게 전달해줘야 할 때 사용!
자바스크립트 메소드에서 파라미터같은 개념이라고 생각하면 된다.
부모 컴포넌트에서 전달한 props는 함수형 컴포넌트에서 함수의 파라미터로 전달받으며, JSX 내부에서 { } 중괄호로 감싸서 사용.</p>
<p><strong>부모 컴포넌트 (App.js) - name props 전달</strong></p>
<pre><code class="language-html">&lt;FuncComponent name=&quot;짱구&quot;&gt;&lt;/FuncComponent name&gt;</code></pre>
<p><strong>자식 컴포넌트 (FuncComponent.js) - name props 받음</strong></p>
<pre><code class="language-javascript">const FuncComponent = (props) =&gt; {
      console.log(props) // {name : &quot;짱구&quot;}
    return (
        &lt;&gt;
        &lt;div&gt; 안녕? {props.name} &lt;/div&gt;
        &lt;/&gt;
    );
}</code></pre>
<p><code>console.log(props)를 해보면 {name : &quot;짱구&quot;}과 같은 결과가 나옴!</code></p>
<h2 id="propschildren">props.children</h2>
<p>부모 컴포넌트에서 자식 컴포넌트를 호출할 때 태그 사이에 작성한 문자열로 컴포넌트 태그 사이에 넣은 값을 조회하고 싶을 땐, props.children 을 조회하면 된다.</p>
<p><strong>부모 컴포넌트</strong></p>
<pre><code class="language-html">function App() {
  return (
        &lt;&gt;
            &lt;Button link=&quot;https://www.google.com&quot;&gt;Google&lt;/Button&gt;
        &lt;/&gt;
  );
}</code></pre>
<p><strong>자식 컴포넌트</strong></p>
<pre><code class="language-javascript">const Button = (props) =&gt; {
  console.log(&quot;Button props &gt;&gt;&gt; &quot;, props);
  // {link: &#39;https://www.google.com&#39;, children: &#39;Google&#39;}
  return (
    &lt;&gt;
      &lt;a href={props.link}&gt;
        &lt;button className=&quot;Button&quot;&gt;{props.children}&lt;/button&gt;
      &lt;/a&gt;
    &lt;/&gt;
  );
};</code></pre>
<p><code>console.log(props)의 결과값은 {link: &#39;https://www.google.com&#39;, children: &#39;Google&#39;} 이다.</code></p>
<p>📌 태그와 태그 사이의 모든 내용을 표시하기 위해 사용되는 특수한 props라고 생각하면 이해하기 쉬울것 같다!
그래도 살짝 헷갈린다 한 가지 예시를 더 보자!!!</p>
<p><strong>부모 컴포넌트</strong></p>
<pre><code class="language-html">const App = () =&gt; (
  &lt;Category&gt;
    &lt;li&gt;First item.&lt;/li&gt;
    &lt;li&gt;Second item.&lt;/li&gt;
    &lt;li&gt;Another item.&lt;/li&gt;
  &lt;/Category&gt;
);</code></pre>
<p><strong>자식 컴포넌트 - 부모 컴포넌트로부터 전달받은 데이터를 사용</strong></p>
<pre><code class="language-javascript">const Category = (props) =&gt; {
  console.log(&quot;Category props &gt;&gt;&gt;&quot;, props);
  // {children: Array(3)}
  // children: Array(3)
  // 0: {$$typeof: Symbol(react.element), type: &#39;li&#39;, key: null, ref: null, props: {…}, …}
  // 1: {$$typeof: Symbol(react.element), type: &#39;li&#39;, key: null, ref: null, props: {…}, …}
  // 2: {$$typeof: Symbol(react.element), type: &#39;li&#39;, key: null, ref: null, props: {…}, …}
  return &lt;ul&gt;{props.children}&lt;/ul&gt;;
};</code></pre>
<p><img src="https://velog.velcdn.com/images/_u__me_with/post/04dfe79f-0420-4f75-be41-a5a795a32050/image.png" alt="">
이처럼 부모 컴포넌트의 태그와 태그 사이의 모든 내용을 표시하기 위해 사용하는것이 props.childred 이다.</p>
<h2 id="마무리하며">마무리하며...</h2>
<p>props.childen 개념이 헷갈렸는데 찾아보면서 정리해보니 이해가 되었다. 이제 리액트 시작이다. 복습 열심히 해서 리액트 마스터 하자!! 💪🏻</p>
<blockquote>
<p>출처 및 참고</p>
</blockquote>
<ol>
<li>[새싹 X 코딩온] 영등포 캠퍼스 6기 입문자도 가능한 웹 개발자 부트캠프 강의 교안</li>
<li>벨로퍼트와 함께하는 모던 리액트 <a href="https://react.vlpt.us/">https://react.vlpt.us/</a></li>
<li><a href="https://developer-talk.tistory.com/226">https://developer-talk.tistory.com/226</a></li>
<li><a href="https://yooneeee.tistory.com/49">https://yooneeee.tistory.com/49</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[팀 프로젝트 회고록]]></title>
            <link>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-%EC%98%81%EB%93%B1%ED%8F%AC-%EC%BA%A0%ED%8D%BC%EC%8A%A4-6%EA%B8%B0-%EC%9E%85%EB%AC%B8%EC%9E%90%EB%8F%84-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%9B%B9-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-1%EC%B0%A8-%ED%8C%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@_u__me_with/%EC%BD%94%EB%94%A9%EC%98%A8-X-%EC%83%88%EC%8B%B9-%EC%98%81%EB%93%B1%ED%8F%AC-%EC%BA%A0%ED%8D%BC%EC%8A%A4-6%EA%B8%B0-%EC%9E%85%EB%AC%B8%EC%9E%90%EB%8F%84-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%9B%B9-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-1%EC%B0%A8-%ED%8C%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Fri, 26 Jul 2024 13:40:21 GMT</pubDate>
            <description><![CDATA[<p>첫 프로젝트가 끝이 났다. 처음이라 걱정도 많았고 어려운 점도 많았지만 팀원들과 함께 잘 마칠 수 있었다. 많은 것을 배우고 느껴 기록해볼려고한다!</p>
<h2 id="1-프로젝트-소개">1. 프로젝트 소개</h2>
<h3 id="blomit">blomit</h3>
<ul>
<li>주제: 개발자를 위한 블로그</li>
<li>주제 선정 이유: &#39;velog&#39;를 사용하면서 느낀 좋았던 점과 개선해볼점을 구현하고싶어 선택</li>
<li>&#39;velog&#39; 장점
전반적으로 깔끔한 인터페이스로 사용하기 편리.
전체 게시물 목록을 카드 형식으로 노출시켜 글이 돋보이는 효과.
개발자들에게 익숙한 마크다운을 사용.
헤더에 프로필과 닉네임을 노출시켜 로그인 상태를 확실히 보여줌.</li>
<li>개선해볼점
Github의 잔디처럼 내가 쓴 게시물의 수를 시각적으로 보여준다면 더 효과적으로 블로그를 쓸 수 있을 것이라고 생각.</li>
</ul>
<h3 id="진행기간">진행기간</h3>
<p>2024.07.10 ~ 2024.07.24 (+ 07.04부터 설계 시작)</p>
<h3 id="팀-구성">팀 구성</h3>
<p>백엔드: 3명 / 프론트엔드: 2명</p>
<h3 id="기술-스택">기술 스택</h3>
<ul>
<li>백엔드
node.js, express, mysql, sequelize</li>
<li>프론트엔드</li>
<li><em>javascript, ejs, axios, tailwind-css*</em></li>
<li>협력
git, github, slack, notion, pigma</li>
<li>기타
postman, chart.js, toast UI</li>
</ul>
<h3 id="구현-기능">구현 기능</h3>
<ol>
<li>회원 관리<ul>
<li>회원가입</li>
<li>로그인/로그아웃</li>
<li>회원정보 확인/수정</li>
</ul>
</li>
<li>게시물 관리<ul>
<li>전체 게시물 목록 조회</li>
<li>내 게시물 목록 조회</li>
<li>게시물 검색</li>
<li>게시물 등록/수정/삭제</li>
<li>월별 업로드 수 차트</li>
</ul>
</li>
<li>댓글 관리<ul>
<li>댓글 등록/수정/삭제</li>
<li>대댓글 등록/수정/삭제</li>
<li>댓글, 대댓글 목록 확인</li>
</ul>
</li>
</ol>
<h2 id="2-내가-맡은-역할">2. 내가 맡은 역할</h2>
<h3 id="포지션">포지션</h3>
<p>프론트엔드</p>
<h3 id="구현-기능-1">구현 기능</h3>
<ol>
<li><p><strong>페이지네이션 컴포넌트</strong></p>
<ul>
<li>백에서 보낸 데이터(총 게시물 수, 총 페이지 수 , 현재 페이지)를 가공하여 페이지네이션의 그룹을 지정하고 계산하여 화면 구성</li>
<li>반복되는 구성으로 컴포넌트로 분리하여 유지보수 효율을 증가</li>
</ul>
</li>
<li><p><strong>전체 게시물 목록</strong></p>
<ul>
<li>forEach문을 사용하여 구현</li>
<li>전체적인 카드형식 디자인 틀 구현</li>
<li>게시물 클릭시 상세페이지로 이동하는 API
<img src="https://velog.velcdn.com/images/_u__me_with/post/a224f3de-c0fe-4e2e-bdc8-c9d1e592d5c8/image.gif" alt=""></li>
</ul>
<ol start="3">
<li><strong>게시물 조회, 내 게시물 삭제</strong><ul>
<li>상세페이지 UI</li>
<li>모든 게시물과 내가 작성한 게시물인지 확인 후 수정/삭제 버튼 보여주기(백에서 보내준 sessionUserId로 확인)</li>
<li>게시물 삭제 API
<img src="https://velog.velcdn.com/images/_u__me_with/post/fe80efdc-8713-419b-bade-f413c798c9d6/image.gif" alt=""></li>
</ul>
</li>
</ol>
</li>
<li><p><strong>댓글 등록/수정/삭제/조회</strong></p>
<ul>
<li>댓글, 대댓글 UI</li>
<li>댓글 등록/수정/삭제 동적 구현</li>
<li>댓글 작성과 등록 메소드 분리(댓글 작성 axios에서 댓글 등록 메소드 실행)</li>
<li>댓글 등록: addEventListener 사용, 댓글 등록 API</li>
<li>댓글 수정: onclick 속성 사용, 입력폼으로 바뀌고 버튼도 바뀌게 구현, 댓글 수정 API</li>
<li>댓글 삭제: onclick 속성 사용, 댓글 삭제 API</li>
</ul>
</li>
<li><p><strong>헤더 컴포넌트</strong></p>
<ul>
<li>header UI</li>
<li>로그인 전과 후를 if문을 이용하여 구분(백에서 보내는 sessionUser를 이용하여 조건 설정)</li>
<li>로그인 전: 로그인 버튼
<img src="https://velog.velcdn.com/images/_u__me_with/post/31fa0eec-7470-4197-bc4d-a770dd8498c1/image.png" alt=""></li>
<li>로그인 후: 사용자 닉네임과 프로필(sessionUser에 담긴 데이터 이용, ), 프로필 클릭시 드롭다운으로 메뉴창 구현(a태그 사용하여 경로 이동)
<img src="https://velog.velcdn.com/images/_u__me_with/post/c5810c95-d9a8-4cb2-a266-a949dfb39bf3/image.png" alt=""></li>
</ul>
</li>
</ol>
<h2 id="3-트러블-슈팅">3. 트러블 슈팅</h2>
<p><strong>&#39; ~ is not defined&#39; 에러!!!</strong>
이번 프로젝트를 하면서 수도없이 본 에러이다.
특히 axios 작성 초반에 가장 많이 봤다. 왜!? 정의가 되어있지않다고할까... 이번 프로젝트에서 힘들었던 점 중 하나였다.</p>
<p><strong>시도한 방법</strong>
오타인지 확인하기
axios 문법 확인하기</p>
<p><strong>해결 방법</strong></p>
<ol>
<li>백에서 보내는 데이터 확인하기(백코드에서 console.log 이용)</li>
<li>프론트에서 백이 보낸 데이터(res)가 오는지 확인하기(프론트 코드에서 console.log 이용)</li>
</ol>
<p><strong>알게된 점</strong></p>
<ol>
<li><strong>백에서 보내는 데이터</strong>가 무엇인지 확인하는 과정이 정말! 중요하니 꼭! 확인하기
이 과정 때문에 프론트개발자이더라도 백엔드 코드를 읽고 이해할 줄 알아야한다는것을 다시 한 번 느꼈다.</li>
<li>넘어온 데이터를 프론트가 가공하기 전에 <strong>응답(res)값</strong>이 왔는지 확인하기!
res의 값을 이용하여 화면에 보여주는 코드를 작성하기 전에 내가 요청한 데이터가 왔는지 확인하는 과정 또한 꼭! 필요하다.</li>
</ol>
<h2 id="4-회고">4. 회고</h2>
<p><strong>4L 회고</strong></p>
<ul>
<li>Liked : 좋았던 점은 무엇인가?</li>
<li>Lacked : 아쉬웠던 점, 부족한 점은 무엇인가?</li>
<li>Learned : 배운 점은 무엇인가?</li>
<li>Longed for : 앞으로 바라는 것은 무엇인가?<h3 id="liked">Liked</h3>
</li>
<li>욕심부리지않은것!
초반에 내가 맡은 분량을 기간 내에 모두 끝낼 수 없을 거같아 팀원들과 나눠가지게 되었다. 내가 모두 구현해보고싶었지만 완성하는 것이 더 중요하기에 욕심부리지않고 팀원의 도움을 받았다.</li>
<li>포기하지않은것!
욕심을 부리진않았지만 열정과 책임감은 놓치않았다. 포기하지않고 시간을 들인다면 해낼 수 있는 부분은 꼭! 구현해내겠다고 이야기하였다. 만약 그 때 내가 해낼 수 없다고 생각하고 손을 놓았더라면 난 성장하지 못하였을것이다.<h3 id="lacked">Lacked</h3>
</li>
<li>의견을 적극적으로 내지않은것
첫 프로젝트라 더욱 소심했고 겁이 났었던 거같다. 아이디어가 좋은 팀원들 덕분에 만족스러운 결과물이지만 나의 의견을 제시해야 생각도 깊어지는 것 같다고 느꼈다.<h3 id="learned">Learned</h3>
</li>
<li>소통
소통에도 적정선이 있다는 것을 느끼게 되었다.
또한 깃 관리에 대해서 알게 된 점이 많다. 팀으로 깃을 사용한 건 처음이었는데 소통이 되어야 코드가 변경되거나 없어지는일이 일어나지않을 수 있다는 것을 알게 되었다.</li>
<li>백과 프론트의 연결
수업이나 과제를 할 때는 백과 프론트 코드를 내가 혼자 구현하기 때문에 백과 프론트를 연결하는 과정에서 어려운 점이 없다고 생각했다. 이번 프로젝트에서 그 역할이 분명히 나눠지다보니 연결하는 과정이 어려웠다. 데이터를 확인하는 과정을 배울 수 있었다.</li>
<li>요청 경로
app.use()로 정의한 기본 경로를 잘 확인해야한다. 라우터에서는 생략되어있지만 프론트에서 axios로 요청할 때는 모든 경로를 써줘야하기 때문이다.<h3 id="longed-for">Longed for</h3>
</li>
<li>백엔드 로직을 더욱 공부하여 이해력을 높여야겠다.</li>
<li>다양한 디자인을 찾아보고 다음 프로젝트 때 구현해봐야겠다.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>