<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>pasongsong gayeon tak!</title>
        <link>https://velog.io/</link>
        <description>pasongsong gayeon tak!</description>
        <lastBuildDate>Tue, 16 Sep 2025 06:27:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>pasongsong gayeon tak!</title>
            <url>https://velog.velcdn.com/images/rk_yeon/profile/0456bd49-3c87-4118-b087-4c5d9382ea62/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. pasongsong gayeon tak!. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/rk_yeon" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[JSP]]></title>
            <link>https://velog.io/@rk_yeon/JSP</link>
            <guid>https://velog.io/@rk_yeon/JSP</guid>
            <pubDate>Tue, 16 Sep 2025 06:27:25 GMT</pubDate>
            <description><![CDATA[<h3 id="jsp란">JSP란?</h3>
<p>Java Server Pages 
= 자바 웹 프로그램
= 자바 기반의 서버 측 웹 프로그래밍 언어
= 자바 백/프엔 다 가능</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Dialog에서 Portal 적용]]></title>
            <link>https://velog.io/@rk_yeon/Dialog%EC%97%90%EC%84%9C-Portal-%EC%A0%81%EC%9A%A9</link>
            <guid>https://velog.io/@rk_yeon/Dialog%EC%97%90%EC%84%9C-Portal-%EC%A0%81%EC%9A%A9</guid>
            <pubDate>Tue, 05 Aug 2025 02:06:40 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>shadcn/ui의 dialog (= Radix 기반)</p>
<pre><code class="language-tsx">  &quot;use client&quot;

  import * as React from &quot;react&quot;
  import * as DialogPrimitive from &quot;@radix-ui/react-dialog&quot;
  import { XIcon } from &quot;lucide-react&quot;

  import { cn } from &quot;@/lib/utils&quot;

  function Dialog({
    ...props
  }: React.ComponentProps&lt;typeof DialogPrimitive.Root&gt;) {
    return &lt;DialogPrimitive.Root data-slot=&quot;dialog&quot; {...props} /&gt;
  }

  function DialogTrigger({
    ...props
  }: React.ComponentProps&lt;typeof DialogPrimitive.Trigger&gt;) {
    return &lt;DialogPrimitive.Trigger data-slot=&quot;dialog-trigger&quot; {...props} /&gt;
  }

  function DialogPortal({
    ...props
  }: React.ComponentProps&lt;typeof DialogPrimitive.Portal&gt;) {
    return &lt;DialogPrimitive.Portal data-slot=&quot;dialog-portal&quot; {...props} /&gt;
  }

  function DialogClose({
    ...props
  }: React.ComponentProps&lt;typeof DialogPrimitive.Close&gt;) {
    return &lt;DialogPrimitive.Close data-slot=&quot;dialog-close&quot; {...props} /&gt;
  }

  function DialogOverlay({
    className,
    ...props
  }: React.ComponentProps&lt;typeof DialogPrimitive.Overlay&gt;) {
    return (
      &lt;DialogPrimitive.Overlay
        data-slot=&quot;dialog-overlay&quot;
        className={cn(
          &quot;data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50&quot;,
          className
        )}
        {...props}
      /&gt;
    )
  }

  function DialogContent({
    className,
    children,
    showCloseButton = true,
    ...props
  }: React.ComponentProps&lt;typeof DialogPrimitive.Content&gt; &amp; {
    showCloseButton?: boolean
  }) {
    return (
      &lt;DialogPortal data-slot=&quot;dialog-portal&quot;&gt;
        &lt;DialogOverlay /&gt;
        &lt;DialogPrimitive.Content
          data-slot=&quot;dialog-content&quot;
          className={cn(
            &quot;bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg&quot;,
            className
          )}
          {...props}
        &gt;
          {children}
          {showCloseButton &amp;&amp; (
            &lt;DialogPrimitive.Close
              data-slot=&quot;dialog-close&quot;
              className=&quot;ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&amp;_svg]:pointer-events-none [&amp;_svg]:shrink-0 [&amp;_svg:not([class*=&#39;size-&#39;])]:size-4&quot;
            &gt;
              &lt;XIcon /&gt;
              &lt;span className=&quot;sr-only&quot;&gt;Close&lt;/span&gt;
            &lt;/DialogPrimitive.Close&gt;
          )}
        &lt;/DialogPrimitive.Content&gt;
      &lt;/DialogPortal&gt;
    )
  }

  function DialogHeader({ className, ...props }: React.ComponentProps&lt;&quot;div&quot;&gt;) {
    return (
      &lt;div
        data-slot=&quot;dialog-header&quot;
        className={cn(&quot;flex flex-col gap-2 text-center sm:text-left&quot;, className)}
        {...props}
      /&gt;
    )
  }

  function DialogFooter({ className, ...props }: React.ComponentProps&lt;&quot;div&quot;&gt;) {
    return (
      &lt;div
        data-slot=&quot;dialog-footer&quot;
        className={cn(
          &quot;flex flex-col-reverse gap-2 sm:flex-row sm:justify-end&quot;,
          className
        )}
        {...props}
      /&gt;
    )
  }

  function DialogTitle({
    className,
    ...props
  }: React.ComponentProps&lt;typeof DialogPrimitive.Title&gt;) {
    return (
      &lt;DialogPrimitive.Title
        data-slot=&quot;dialog-title&quot;
        className={cn(&quot;text-lg leading-none font-semibold&quot;, className)}
        {...props}
      /&gt;
    )
  }

  function DialogDescription({
    className,
    ...props
  }: React.ComponentProps&lt;typeof DialogPrimitive.Description&gt;) {
    return (
      &lt;DialogPrimitive.Description
        data-slot=&quot;dialog-description&quot;
        className={cn(&quot;text-muted-foreground text-sm&quot;, className)}
        {...props}
      /&gt;
    )
  }

  export {
    Dialog,
    DialogClose,
    DialogContent,
    DialogDescription,
    DialogFooter,
    DialogHeader,
    DialogOverlay,
    DialogPortal,
    DialogTitle,
    DialogTrigger,
  }
</code></pre>
</li>
</ul>
<pre><code class="language-tsx">function DialogContent({
  className,
  children,
  showCloseButton = true,
  ...props
}: React.ComponentProps&lt;typeof DialogPrimitive.Content&gt; &amp; {
  showCloseButton?: boolean
}) {
  return (
    &lt;DialogPortal data-slot=&quot;dialog-portal&quot;&gt;
      &lt;DialogOverlay /&gt;
      &lt;DialogPrimitive.Content
        .....</code></pre>
<ul>
<li><p><code>DialogContent</code> 안에서 <code>DialogPortal</code>을 자동으로 사용하고 있음</p>
<p>  ⇒ shadcn/ui의 디자인 패턴</p>
</li>
</ul>
<h3 id="portal의-장점들"><strong>Portal의 장점들</strong></h3>
<ol>
<li><strong>DOM 계층 구조 탈출</strong>: 다이얼로그가 현재 컴포넌트의 DOM 위치와 관계없이 document.body에 렌더링됨</li>
<li><strong>z-index 문제 해결</strong>: 부모 요소의 overflow: hidden이나 z-index 제약을 받지 않음</li>
<li><strong>접근성 향상</strong>: 스크린 리더가 다이얼로그를 올바르게 인식하고 포커스 관리가 개선됨</li>
<li><strong>CSS 스타일링 독립성</strong>: 부모 컴포넌트의 CSS 스타일에 영향받지 않음</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[3. 모노레포(Monorepo)가 뭔데]]></title>
            <link>https://velog.io/@rk_yeon/3.-%EB%AA%A8%EB%85%B8%EB%A0%88%ED%8F%ACMonorepo%EA%B0%80-%EB%AD%94%EB%8D%B0</link>
            <guid>https://velog.io/@rk_yeon/3.-%EB%AA%A8%EB%85%B8%EB%A0%88%ED%8F%ACMonorepo%EA%B0%80-%EB%AD%94%EB%8D%B0</guid>
            <pubDate>Tue, 05 Aug 2025 01:59:50 GMT</pubDate>
            <description><![CDATA[<h3 id="정의">정의</h3>
<p>모노레포(Monorepo)는 <strong>단일(Mono) 저장소(Repository)</strong>의 줄임말</p>
<p>소프트웨어 개발에서 관련된 여러 프로젝트의 소스 코드를 <strong>하나의 버전 관리 저장소에 모두 모아 관리하는 전략</strong>을 의미</p>
<p>이는 각 프로젝트마다 별도의 저장소를 사용하는 &#39;폴리레포(Polyrepo)&#39; 방식과 대조됨</p>
<p>과거에는 거대한 단일 애플리케이션을 의미하는 &#39;모놀리식(Monolithic)&#39;과 혼동되기도 했지만, 모노레포는 여러 개의 개별 프로젝트가 잘 정의된 관계를 가지고 한곳에 모여 있는 형태라는 점에서 차이가 있음</p>
<h3 id="모노레포의-주요-특징-및-장점">모노레포의 주요 특징 및 장점</h3>
<p>모노레포는 여러 프로젝트를 한곳에서 관리함으로써 다양한 이점을 제공함</p>
<ul>
<li><strong>코드 공유 및 재사용 용이:</strong>
모든 코드가 한곳에 있어 공통 라이브러리나 컴포넌트를 쉽게 공유하고 재사용할 수 있습니다. 이는 코드 중복을 줄이고 일관성을 유지하는 데 도움이 됩니다.</li>
<li><strong>원자적 커밋(Atomic Commits):</strong>
여러 프로젝트에 걸친 변경 사항을 단일 커밋으로 묶을 수 있습니다. 예를 들어, API와 클라이언트의 변경이 동시에 필요한 경우, 하나의 커밋으로 처리하여 두 프로젝트 간의 동기화를 보장하고 잠재적인 오류를 방지할 수 있습니다.</li>
<li><strong>간소화된 의존성 관리:</strong>
모든 프로젝트가 동일한 저장소에 있으므로 외부 라이브러리의 버전을 통일하기 쉽습니다. 이로 인해 &#39;의존성 지옥(Dependency Hell)&#39;과 같은 복잡한 버전 충돌 문제를 피할 수 있습니다.</li>
<li><strong>향상된 협업 및 투명성:</strong>
모든 개발자가 전체 코드베이스에 접근할 수 있어 팀 간의 협업이 원활해지고, 다른 팀의 작업을 쉽게 확인하며 지식을 공유할 수 있습니다.</li>
<li><strong>통합된 개발 환경 및 도구:</strong>
모든 프로젝트에 동일한 빌드, 테스트, 배포 도구를 적용하기 용이하여 개발 환경을 표준화하고 유지보수 비용을 절감할 수 있습니다.</li>
</ul>
<hr>
<h3 id="단점-및-고려사항">단점 및 고려사항</h3>
<p>도입 시 신중한 고려가 필요</p>
<ul>
<li><strong>저장소 크기 및 성능 저하:</strong>
프로젝트 수가 늘어날수록 저장소의 전체 크기가 커져 처음 저장소를 복제(clone)하는 데 시간이 오래 걸릴 수 있으며, Git과 같은 버전 관리 시스템의 성능에 영향을 줄 수 있습니다.</li>
<li><strong>빌드 및 테스트 시간 증가:</strong>
코드베이스가 커짐에 따라 모든 프로젝트를 빌드하고 테스트하는 데 걸리는 시간이 길어질 수 있습니다. 이를 해결하기 위해 영향을 받은 프로젝트만 선택적으로 빌드하는 등의 최적화된 도구(예: Bazel, Nx)가 필요합니다.</li>
<li><strong>복잡성 증가:</strong>
저장소 내의 프로젝트가 많아질수록 전체 구조를 파악하고 관리하는 것이 복잡해질 수 있습니다. 명확한 디렉토리 구조와 규칙 설정이 중요합니다.</li>
<li><strong>접근 제어의 어려움:</strong>
기본적으로 모든 코드에 대한 접근이 가능하므로, 특정 프로젝트에 대한 접근 권한을 세밀하게 제어하기 어려울 수 있습니다.</li>
</ul>
<hr>
<h3 id="모노레포-vs-폴리레포">모노레포 vs 폴리레포</h3>
<table>
<thead>
<tr>
<th>구분</th>
<th><strong>모노레포 (Monorepo)</strong></th>
<th><strong>폴리레포 (Polyrepo)</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>저장소 구조</strong></td>
<td>여러 프로젝트를 하나의 저장소에서 관리</td>
<td>프로젝트마다 별도의 저장소 사용</td>
</tr>
<tr>
<td><strong>코드 공유</strong></td>
<td>매우 용이</td>
<td>패키지 매니저를 통한 버전 관리 필요</td>
</tr>
<tr>
<td><strong>의존성 관리</strong></td>
<td>단일화 및 간소화</td>
<td>프로젝트별로 개별 관리, 버전 충돌 가능성</td>
</tr>
<tr>
<td><strong>변경 관리</strong></td>
<td>원자적 커밋으로 여러 프로젝트 동시 변경</td>
<td>여러 저장소에 걸쳐 개별적인 커밋 및 PR 필요</td>
</tr>
<tr>
<td><strong>팀 자율성</strong></td>
<td>낮음 (통일된 표준 강조)</td>
<td>높음 (팀별로 독립적인 개발 방식 가능)</td>
</tr>
<tr>
<td><strong>대표적인 장점</strong></td>
<td>코드 재사용성, 일관성, 간소화된 협업</td>
<td>독립적인 개발 및 배포, 작은 저장소 크기</td>
</tr>
</tbody></table>
<hr>
<h3 id="모노레포를-사용하는-대표적인-기업">모노레포를 사용하는 대표적인 기업</h3>
<p>거대 테크 기업들을 중심으로 많은 곳에서 모노레포의 장점을 활용하고 있습니다.</p>
<ul>
<li><strong>구글 (Google):</strong> 거의 모든 소프트웨어의 소스 코드를 거대한 단일 모노레포에서 관리하는 것으로 유명합니다. 이를 위해 자체 버전 관리 시스템인 &#39;파이퍼(Piper)&#39;와 빌드 시스템 &#39;바젤(Bazel)&#39;을 개발했습니다.</li>
<li><strong>메타 (Meta / 구 Facebook):</strong> 페이스북, 인스타그램 등 주요 서비스의 코드를 모노레포에서 관리하며 빠른 개발 속도와 팀 간 협업을 도모하고 있습니다.</li>
<li><strong>마이크로소프트 (Microsoft):</strong> 윈도우(Windows), 오피스(Office)와 같은 대규모 프로젝트에서 모노레포를 사용하여 코드베이스의 일관성과 협업 효율성을 높이고 있습니다.</li>
<li><strong>트위터 (Twitter):</strong> 여러 핵심 서비스를 모노레포 환경에서 개발하고 있습니다.</li>
</ul>
<p>결론적으로 모노레포는 여러 프로젝트가 긴밀하게 연관되어 있고, 코드 공유와 일관성 있는 개발 문화가 중요한 대규모 조직 및 프로젝트에 강력한 이점을 제공하는 개발 전략입니다. 하지만 성공적인 도입을 위해서는 그에 따른 기술적, 문화적 과제를 해결하기 위한 노력이 동반되어야 합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Radix UI의 특별한 prop, asChild]]></title>
            <link>https://velog.io/@rk_yeon/Radix-UI%EC%9D%98-%ED%8A%B9%EB%B3%84%ED%95%9C-prop-asChild</link>
            <guid>https://velog.io/@rk_yeon/Radix-UI%EC%9D%98-%ED%8A%B9%EB%B3%84%ED%95%9C-prop-asChild</guid>
            <pubDate>Tue, 05 Aug 2025 01:54:55 GMT</pubDate>
            <description><![CDATA[<h3 id="aschild의-역할">asChild의 역할</h3>
<p><strong><code>asChild={true}</code>일 때:</strong></p>
<ul>
<li>Radix UI 컴포넌트가 자체 DOM 요소를 렌더링하지 않음</li>
<li>대신 자식 요소의 props를 받아서 그 자식 요소에 Radix UI의 기능을 전달</li>
<li>자식 요소가 실제 DOM 요소가 됨</li>
</ul>
<p><strong><code>asChild={false}</code> 또는 생략할 때:</strong></p>
<ul>
<li>Radix UI 컴포넌트가 자체 DOM 요소를 렌더링</li>
<li>추가적인 wrapper div가 생성됨</li>
</ul>
<hr>
<h3 id="예시">예시</h3>
<pre><code class="language-tsx">// asChild={true} - Button이 실제 DOM 요소가 됨
&lt;DialogTrigger asChild&gt;
  &lt;Button&gt;열기&lt;/Button&gt;
&lt;/DialogTrigger&gt;

// asChild 없음 - 추가 div가 생성됨
&lt;DialogTrigger&gt;
  &lt;Button&gt;열기&lt;/Button&gt;
&lt;/DialogTrigger&gt;
</code></pre>
<h3 id="차이점">차이점</h3>
<ul>
<li><strong>asChild 있음</strong>: <code>&lt;button&gt;열기&lt;/button&gt;</code> (깔끔함)</li>
<li><strong>asChild 없음</strong>: <code>&lt;div&gt;&lt;button&gt;열기&lt;/button&gt;&lt;/div&gt;</code> (불필요한 wrapper)</li>
</ul>
<p>따라서 <code>asChild</code>를 사용하면 불필요한 DOM 요소 없이 깔끔한 HTML 구조를 만들 수 있음</p>
<hr>
<aside>

<h3 id="html-중첩-문제">HTML 중첩 문제</h3>
</aside>

<ul>
<li><p>오류 코드</p>
<pre><code class="language-tsx">
  dialog.tsx:38 &lt;button&gt; cannot contain a nested &lt;button&gt;.

  See this log for the ancestor stack trace.

  react-dom-client.development.js:4507 Uncaught Error: Hydration failed because the server rendered HTML didn&#39;t match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:

  - A server/client branch `if (typeof window !== &#39;undefined&#39;)`.
  - Variable input such as `Date.now()` or `Math.random()` which changes each time it&#39;s called.
  - Date formatting in a user&#39;s locale which doesn&#39;t match the server.
  - External changing data without sending a snapshot of it along with the HTML.
  - Invalid HTML tag nesting.

  It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.

  https://react.dev/link/hydration-mismatch

  at throwOnHydrationMismatch (react-dom-client.development.js:4507:11)

  at beginWork (react-dom-client.development.js:10928:17)

  at runWithFiberInDEV (react-dom-client.development.js:872:30)

  at performUnitOfWork (react-dom-client.development.js:15677:22)

  at workLoopConcurrentByScheduler (react-dom-client.development.js:15671:9)

  at renderRootConcurrent (react-dom-client.development.js:15646:15)

  at performWorkOnRoot (react-dom-client.development.js:14940:13)

  at performWorkOnRootViaSchedulerTask (react-dom-client.development.js:16766:7)

  at MessagePort.performWorkUntilDeadline (scheduler.development.js:45:48)</code></pre>
</li>
<li><p>원인 ⇒ DialogTrigger 안에 Button 컴포넌트를 사용할 때 발생하는 HTML 중첩 문제</p>
<p>  = DialogTrigger는 이미 <code>button</code> 요소를 렌더링하는데, 그 안에 또 다른 <code>button</code> 요소인 Button 컴포넌트가 들어가서 발생하는 문제</p>
</li>
<li><p>해결 방법 ⇒ DialogTrigger에 asChild prop을 사용</p>
<ul>
<li><p>코드</p>
<pre><code class="language-tsx">  &lt;Dialog&gt;
      &lt;DialogTrigger asChild&gt;
          &lt;Button&gt;다이얼로그 열기&lt;/Button&gt;
      &lt;/DialogTrigger&gt;
      &lt;DialogContent&gt;
          &lt;DialogHeader&gt;
              &lt;DialogTitle&gt;테스트 다이얼로그&lt;/DialogTitle&gt;
              &lt;DialogDescription&gt;이것은 Dialog 컴포넌트의 동작을 테스트하는 예시입니다.&lt;/DialogDescription&gt;
          &lt;/DialogHeader&gt;
          &lt;div className=&quot;mt-4&quot;&gt;
              &lt;p&gt;여기에 원하는 내용을 넣을 수 있습니다.&lt;/p&gt;
          &lt;/div&gt;
          &lt;DialogFooter&gt;
              &lt;DialogClose asChild&gt;
                  &lt;div className=&quot;flex gap-2&quot;&gt;
                      &lt;Button variant=&quot;primary&quot; outline={true}&gt;
                          취소
                      &lt;/Button&gt;
                      &lt;Button variant=&quot;primary&quot;&gt;확인&lt;/Button&gt;
                  &lt;/div&gt;
              &lt;/DialogClose&gt;
          &lt;/DialogFooter&gt;
      &lt;/DialogContent&gt;
  &lt;/Dialog&gt;</code></pre>
</li>
</ul>
</li>
</ul>
<h3 id="주요-변경사항">주요 변경사항:</h3>
<ol>
<li><p><strong>DialogTrigger에 asChild 추가</strong>: 이렇게 하면 DialogTrigger가 자체 <code>button</code>을 렌더링하지 않고, 자식 요소(Button)를 그대로 사용함</p>
<ol start="2">
<li><strong>DialogClose에도 asChild 추가</strong>: 마찬가지로 닫기 버튼도 중첩을 방지함</li>
<li><strong>Button 컴포넌트 사용</strong>: 커스텀 <code>button</code> 대신 프로젝트의 <code>Button</code> 컴포넌트를 사용</li>
</ol>
<p>이렇게 하면 HTML 중첩 문제가 해결되고 hydration 오류가 발생하지 않음</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[1일차 회고-기획 및 설계]]></title>
            <link>https://velog.io/@rk_yeon/%EC%82%AC%EC%9D%B4%EB%93%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-1%EC%9D%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-%EA%B8%B0%ED%9A%8D-%EB%B0%8F-%EC%84%A4%EA%B3%84</link>
            <guid>https://velog.io/@rk_yeon/%EC%82%AC%EC%9D%B4%EB%93%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-1%EC%9D%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-%EA%B8%B0%ED%9A%8D-%EB%B0%8F-%EC%84%A4%EA%B3%84</guid>
            <pubDate>Tue, 03 Jun 2025 10:28:34 GMT</pubDate>
            <description><![CDATA[<h2 id="안녕하세요">안녕하세요?</h2>
<p><img src="https://velog.velcdn.com/images/rk_yeon/post/7627f982-4f77-4a38-90eb-2c554298673a/image.gif" alt="박명수_썸네일"></p>
<p>싸피에서 3번의 프로젝트를 끝냈습니다.
근데 아쉬움이 남아서 사이드 프로젝트를 시작했습니다.
기간은 <strong>2025.06.02 - 2025.06.11 (10일)</strong> 로 잡았구요.
사실 어제 시작했지만 회고는 오늘부터 쓸 예정입니다.</p>
<hr>
<h2 id="기획">기획</h2>
<p><span style='background-color: #fff5b1'>AI 프롬프팅을 통한 답변 자동화 1:1 채팅 서비스</span></p>
<hr>
<h2 id="기술스택">기술스택</h2>
<ul>
<li>프론트엔드 : <code>React</code>, <code>TypeScript</code>, <code>Tailwind CSS</code>, <code>TenStack-Query(React-Query)</code></li>
<li>백엔드 : <code>Spring Boot</code>, <code>Spring Security</code>, <code>OAuth2</code>, <code>JWT</code>, <code>JPA</code></li>
<li>데이터베이스 : <code>MySQL</code>, <code>MongoDB</code></li>
<li>배포 및 운영 : <code>AWS(EC2)</code>, <code>AWS(S3)</code>, <code>Docker</code>, <code>nginx</code>, <code>Jenkins</code></li>
<li>기타 : <code>WebSocket</code>, <code>Kafka</code>, <code>STOMP.js</code>, <code>LangChain</code></li>
</ul>
<hr>
<h2 id="회고">회고</h2>
<blockquote>
<p><strong>1. 오늘 작업한 거</strong></p>
</blockquote>
<p><span style='background-color: #fff5b1'><strong><a href="https://abalone-vault-d21.notion.site/20708a5de3898062b673f41d5d74539e?source=copy_link">노션 링크</a></strong></span></p>
<ul>
<li><a href="https://abalone-vault-d21.notion.site/20708a5de38980ceba79c085435c7da3?source=copy_link">기능 명세서</a></li>
<li><a href="https://abalone-vault-d21.notion.site/API-20708a5de389802bac8ff342cc56831a?source=copy_link">API 명세서</a></li>
</ul>
<blockquote>
<p><strong>2. 문제 발생했던 거</strong>
<strong>3. 해결 방법 + 배운 점</strong></p>
</blockquote>
<p><span style='color: #663399'><strong>1) 왜 URL에 <code>/api/</code>를 붙이는가?</strong></span>
&quot;<span style='background-color: #fff5b1'>해당 URL이 웹사이트의 사용자 인터페이스(UI)가 아니라 API용 endpoint임을 명시하기 위해</span>&quot;</p>
<p>예시:</p>
<ul>
<li><code>/home</code> → 일반 웹페이지 (HTML 렌더링)</li>
<li><code>/api/home</code> → 데이터만 주고받는 API</li>
</ul>
<p><span style='color: #663399'><strong>2) 왜 회원 관련 api 작성시, <code>/user</code>가 아니라 <code>/users</code>를 쓰는가?</strong></span>
&quot;<span style='background-color: #fff5b1'>RESTful API에서는 리소스를 명사형 복수로 표현하는 것이 표준 관례</span>&quot;
컬렉션(집합) 단위로 리소스를 표현하기 때문에</p>
<p>예시:</p>
<ul>
<li><code>/api/users</code> → 사용자 목록 (GET), 사용자 생성 (POST)
<code>/users</code>는 &quot;사용자 목록&quot;이라는 컬렉션</li>
<li><code>/api/users/123</code> → 특정 사용자 (GET, PUT, DELETE)
<code>/users/123</code>은 &quot;사용자 중 ID가 123인 사람&quot;이라는 단일 리소스</li>
</ul>
<p>결론은...
<span style='color: #663399'><strong>둘다 관례적인 거다~</strong></span></p>
<p><span style='color: #663399'><strong>3) <code>/pub</code> 은 앞에 <code>/app</code>이 붙는데, <code>/sub</code>은 왜 안 붙지?</strong></span>
<img src="https://velog.velcdn.com/images/rk_yeon/post/71c8ca6d-e925-4825-9fc2-949651b87885/image.png" alt="채팅_api"></p>
<p>&quot;<span style='background-color: #fff5b1'>Spring WebSocket/STOMP의 구조상 차이</span>&quot;</p>
<p><code>/app</code>은 애플리케이션 목적지(application destination)의 접두사이고,
<code>/sub</code>은 브로커 목적지(broker destination)의 접두사라서</p>
<p>STOMP 표준 패턴
<code>/app/pub/chat-room/{chatroomId}</code>
<code>/sub/chat-room-list/{userId}</code></p>
<blockquote>
<p><strong>4. 내일 할 거</strong></p>
</blockquote>
<ul>
<li>ERD</li>
<li>ProtoType 겸 Mockup 겸 WireFrame</li>
<li>초기 세팅</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Dart 기본기]]></title>
            <link>https://velog.io/@rk_yeon/Dart-%EA%B8%B0%EB%B3%B8%EA%B8%B0</link>
            <guid>https://velog.io/@rk_yeon/Dart-%EA%B8%B0%EB%B3%B8%EA%B8%B0</guid>
            <pubDate>Sun, 09 Mar 2025 15:57:06 GMT</pubDate>
            <description><![CDATA[<pre><code>void main() {
    var 변수
    int 정수
    double 실수
    bool True/False
    String 글자(대문자로 적어야됨)
    dynamic 기존 선언 타입과 상관없이 아무거나 변경 가능 (ex. int -&gt; String)

    print(변수명.runtimeType) : 변수의 타입 확인 가능
    print(&quot;${변수명}&quot;)
}</code></pre><pre><code>타입 변수명 = &quot;내용물&quot;;
=&gt; null 불가
타입? 변수명 = &quot;내용물&quot;;
=&gt; null 가능

ex.
String name = &quot;어찌고&quot;;
name = null;
print(name); =&gt; 에러 발생

String name2 = &quot;저찌고&quot;;
name2 = null;
print(name2); =&gt; null 출력</code></pre><pre><code>print(name2!);
=&gt; !의 의미 : 현재 변수의 값은 null이 아니다</code></pre><pre><code>final/const 타입 변수명
=&gt; 변수 값 변경 불가

final/const (타입) 변수명
=&gt; 타입 생략 가능</code></pre><pre><code>DateTime now = DateTime.now();

print(now);
=&gt; 코드가 실행되는 순간의 시간

final =&gt; 가능
const =&gt; 불가능

무슨 차이?
BuildTime 값 = 실행되는 순간의 값
final =&gt; 알지 못해도 사용 가능
const =&gt; 모르면 사용 불가</code></pre><p>아오 잠와.. 나중에 마저 쓸게요</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Dart가 뭔데]]></title>
            <link>https://velog.io/@rk_yeon/Dart%EA%B0%80-%EB%AD%94%EB%8D%B0</link>
            <guid>https://velog.io/@rk_yeon/Dart%EA%B0%80-%EB%AD%94%EB%8D%B0</guid>
            <pubDate>Sat, 08 Mar 2025 08:03:38 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요
썸네일 귀엽죠? 춘식이 안 좋아하는데, 이건 너무 귀엽게 생겨서 구매할 예정입니다.
팔다리 하찮게 생긴게..참 매력있네요</p>
<p>이번 프로젝트에서 Flutter를 써보게 되었습니다.. 그래서 Dart 배워야 됨
팀원이 800페이지짜리 책 빌려줘서 눈물이 남.. 고마운데 두꺼워서..</p>
<p>여튼 그래서</p>
<h3 id="다트가-뭐냐">다트가 뭐냐?</h3>
<p>구글이 자바스크립트를 이겨먹기 위해 만든 프로그래밍 언어</p>
<h3 id="그래서-이겼나">그래서 이겼나?</h3>
<p>다트 언어를 자바스크립트로 완전 컴파일 할 수 있는 정도에 그침</p>
<h3 id="근데">근데?</h3>
<p>웹은 그랬지만, 모바일 영역에서 인기 쩐다 ㄷㄷ</p>
<h3 id="특징">특징</h3>
<ul>
<li>UI 제작에 최적화</li>
<li>완전한 비동기 언어 &amp; 이벤트 기반</li>
<li>아이솔레이트를 이용한 동시성 기능 제공</li>
<li>Null 안전성, 스프레드 기능, 컬렉션 if문 등 효율적인 UI 코딩 기능 제공</li>
<li>핫 리로드를 통해 코드 변경 사항 -&gt; 즉시 화면 반영</li>
<li>멀티 플랫폼에서 로깅 및 디버깅 가능</li>
<li>AOT 컴파일 가능 -&gt; 어떤 플랫폼이든 빠른 속도</li>
<li>백엔드 프로그래밍 지원</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Redux가 뭔데]]></title>
            <link>https://velog.io/@rk_yeon/Redux%EA%B0%80-%EB%AD%94%EB%8D%B0</link>
            <guid>https://velog.io/@rk_yeon/Redux%EA%B0%80-%EB%AD%94%EB%8D%B0</guid>
            <pubDate>Sat, 01 Feb 2025 17:35:47 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/rk_yeon/post/74e7a808-db10-4576-93e8-d8a02ed54716/image.png" alt="벨로그 초기 문구 아련하네..">
벨로그 마크다운 형식으로 작성해야되는 거 캣에바네요 ㄷㄷ</p>
<p>Redux가 state로 상태를 관리할 때 계속 props로 넘겨줘야되는데
그 과정에서 데이터가 훼손될 수도 있고, 일관성 유지가 어려울 수도 있음
=&gt; 컴포넌트가 여러 중첩 상태일 때 : 전역 상태를 사용하면 (= Redux씨를 사용하면) 필요한 컴포넌트에서 바로 데이터 가져오기 가능</p>
<h4 id="어떻게-하냐">어떻게 하냐?</h4>
<p>store 라는 곳에 slice로 state를 관리함
= 전역에서 사용 가능!</p>
<h4 id="대충-정리해보면">대충 정리해보면..</h4>
<p>slice : 계속 바뀌지만 모아두면 써먹을 데가 있는 데이터들 (북마크한 영화의 id)
store : 영화 id, 좋아하는 과일 id... 이것저것 slice들의 모음
계속 바뀌는데 쓸모 있는 데이터만 모아서 그때 그때 갖다주는게 Redux씨 ㅇㅋ</p>
<p>덕스씨 피곤한 사람이네..
회원 가입/로그인/수정 다 덕스씨께 드리자
슬라이스 안에 메서드(createUser/updateUser) 등등 써서 관리하면 될듯</p>
]]></description>
        </item>
    </channel>
</rss>