<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>00kang_jh.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Thu, 22 Jan 2026 02:57:43 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>00kang_jh.log</title>
            <url>https://velog.velcdn.com/images/00kang_jh/profile/9f9ca2f0-9508-4d6c-a304-a5a4d9e4ff1c/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 00kang_jh.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/00kang_jh" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Playwright 테스트 실행 옵션 가이드]]></title>
            <link>https://velog.io/@00kang_jh/Playwright-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%A4%ED%96%89-%EC%98%B5%EC%85%98-%EA%B0%80%EC%9D%B4%EB%93%9C</link>
            <guid>https://velog.io/@00kang_jh/Playwright-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%A4%ED%96%89-%EC%98%B5%EC%85%98-%EA%B0%80%EC%9D%B4%EB%93%9C</guid>
            <pubDate>Thu, 22 Jan 2026 02:57:43 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>이전 글에서 Playwright 설치를 완료했습니다. 이번 글에서는 설치 시 생성된 기본 예제 코드를 활용해서 다양한 테스트 실행 옵션을 알아보겠습니다.</p>
<blockquote>
<p>💻 <strong>작성 환경</strong></p>
<ul>
<li>macOS Tahoe 26.2</li>
<li>VS Code</li>
<li>Node.js</li>
</ul>
</blockquote>
<hr>
<h2 id="기본-예제-코드-살펴보기">기본 예제 코드 살펴보기</h2>
<p>Playwright 설치 시 <code>tests/example.spec.ts</code> 파일이 자동으로 생성됩니다.</p>
<pre><code class="language-typescript">import { test, expect } from &#39;@playwright/test&#39;;

test(&#39;has title&#39;, async ({ page }) =&gt; {
  await page.goto(&#39;https://playwright.dev/&#39;);

  // Expect a title &quot;to contain&quot; a substring.
  await expect(page).toHaveTitle(/Playwright/);
});

test(&#39;get started link&#39;, async ({ page }) =&gt; {
  await page.goto(&#39;https://playwright.dev/&#39;);

  // Click the get started link.
  await page.getByRole(&#39;link&#39;, { name: &#39;Get started&#39; }).click();

  // Expects page to have a heading with the name of Installation.
  await expect(page.getByRole(&#39;heading&#39;, { name: &#39;Installation&#39; })).toBeVisible();
});</code></pre>
<p>이 코드는 Playwright 공식 사이트에 접속해서 타이틀을 확인하고, &quot;Get started&quot; 링크를 클릭하는 간단한 테스트입니다. 이 예제를 기준으로 다양한 실행 옵션을 살펴보겠습니다.</p>
<hr>
<h2 id="테스트-실행-옵션">테스트 실행 옵션</h2>
<h3 id="기본-실행">기본 실행</h3>
<pre><code class="language-bash">npx playwright test</code></pre>
<p>모든 테스트 파일을 실행합니다. 기본적으로 headless 모드(브라우저 창 없이)로 실행되며, 설정된 모든 브라우저(Chromium, Firefox, WebKit)에서 테스트가 진행됩니다.</p>
<p><img src="https://velog.velcdn.com/images/00kang_jh/post/7ea6495e-ce33-4037-8861-68e46a9bd001/image.png" alt=""></p>
<h3 id="worker-수-지정">Worker 수 지정</h3>
<pre><code class="language-bash">npx playwright test --workers 3</code></pre>
<p>병렬로 실행할 worker 수를 지정합니다. 기본값은 CPU 코어 수의 절반입니다. 테스트가 많을 때 worker 수를 늘리면 전체 실행 시간을 단축할 수 있습니다. 반대로 리소스가 부족하거나 테스트 간 충돌이 발생하면 worker 수를 줄여볼 수 있습니다.</p>
<h3 id="특정-파일-실행">특정 파일 실행</h3>
<pre><code class="language-bash">npx playwright test one.spec.ts</code></pre>
<p>특정 테스트 파일만 실행합니다. 파일명 일부만 입력해도 매칭되는 파일을 찾아 실행합니다.</p>
<pre><code class="language-bash">npx playwright test one.spec.ts two.spec.ts</code></pre>
<p>여러 파일을 지정하려면 공백으로 구분해서 나열하면 됩니다.</p>
<h3 id="파일명-키워드로-실행">파일명 키워드로 실행</h3>
<pre><code class="language-bash">npx playwright test one two</code></pre>
<p>파일명에 <code>one</code> 또는 <code>two</code>가 포함된 모든 테스트 파일을 실행합니다. 확장자 없이 키워드만 입력해도 됩니다.</p>
<h3 id="테스트-이름으로-실행">테스트 이름으로 실행</h3>
<pre><code class="language-bash">npx playwright test -g &quot;test title&quot;</code></pre>
<p><code>-g</code> 옵션을 사용하면 테스트 이름(title)으로 필터링할 수 있습니다. 예제 코드 기준으로 <code>&quot;has title&quot;</code> 테스트만 실행하고 싶다면 다음과 같이 입력합니다.</p>
<pre><code class="language-bash">npx playwright test -g &quot;has title&quot;</code></pre>
<p>정규식 패턴도 사용 가능합니다.</p>
<h3 id="특정-브라우저에서-실행">특정 브라우저에서 실행</h3>
<pre><code class="language-bash">npx playwright test --project=chromium</code></pre>
<p>특정 브라우저에서만 테스트를 실행합니다. <code>playwright.config.js</code>에 정의된 프로젝트 이름을 사용합니다.</p>
<table>
<thead>
<tr>
<th>옵션</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>--project=chromium</code></td>
<td>Chrome 기반 브라우저</td>
</tr>
<tr>
<td><code>--project=firefox</code></td>
<td>Firefox</td>
</tr>
<tr>
<td><code>--project=webkit</code></td>
<td>Safari 기반 브라우저</td>
</tr>
</tbody></table>
<p><img src="https://velog.velcdn.com/images/00kang_jh/post/471db6df-f8d8-4a62-8d37-959a3ea386a4/image.png" alt=""></p>
<h3 id="브라우저-창-띄워서-실행">브라우저 창 띄워서 실행</h3>
<pre><code class="language-bash">npx playwright test --headed</code></pre>
<p>기본적으로 Playwright는 headless 모드로 실행되어 브라우저 창이 보이지 않습니다. <code>--headed</code> 옵션을 사용하면 실제 브라우저 창이 열리면서 테스트가 진행되는 모습을 눈으로 확인할 수 있습니다.</p>
<h3 id="디버그-모드-실행">디버그 모드 실행</h3>
<pre><code class="language-bash">npx playwright test --debug</code></pre>
<p>디버그 모드로 실행합니다. Playwright Inspector가 열리면서 테스트를 단계별로 실행하고, 각 단계에서 멈춰서 상태를 확인할 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/00kang_jh/post/b6140b43-278c-4df5-8f43-e873f4972656/image.png" alt=""></p>
<h3 id="특정-라인부터-디버그">특정 라인부터 디버그</h3>
<pre><code class="language-bash">npx playwright test one.spec.ts:11 --debug</code></pre>
<p>파일명 뒤에 <code>:라인번호</code>를 붙이면 해당 라인에 있는 테스트부터 디버그 모드로 실행합니다. 특정 테스트만 집중적으로 디버깅할 때 유용합니다.</p>
<hr>
<h2 id="옵션-조합-예시">옵션 조합 예시</h2>
<p>여러 옵션을 조합해서 사용할 수 있습니다.</p>
<pre><code class="language-bash"># Chromium에서 headed 모드로 특정 파일 실행
npx playwright test example.spec.ts --project=chromium --headed

# worker 1개로 순차 실행하면서 디버그
npx playwright test --workers 1 --debug

# 특정 테스트만 Firefox에서 실행
npx playwright test -g &quot;has title&quot; --project=firefox</code></pre>
<hr>
<h2 id="실행-옵션-요약">실행 옵션 요약</h2>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>npx playwright test</code></td>
<td>전체 테스트 실행</td>
</tr>
<tr>
<td><code>npx playwright test --workers 3</code></td>
<td>worker 수 지정</td>
</tr>
<tr>
<td><code>npx playwright test one.spec.ts</code></td>
<td>특정 파일 실행</td>
</tr>
<tr>
<td><code>npx playwright test one two</code></td>
<td>키워드로 파일 필터링</td>
</tr>
<tr>
<td><code>npx playwright test -g &quot;test title&quot;</code></td>
<td>테스트 이름으로 필터링</td>
</tr>
<tr>
<td><code>npx playwright test --project=chromium</code></td>
<td>특정 브라우저 실행</td>
</tr>
<tr>
<td><code>npx playwright test --headed</code></td>
<td>브라우저 창 띄워서 실행</td>
</tr>
<tr>
<td><code>npx playwright test --debug</code></td>
<td>디버그 모드 실행</td>
</tr>
<tr>
<td><code>npx playwright test file.spec.ts:11 --debug</code></td>
<td>특정 라인부터 디버그</td>
</tr>
</tbody></table>
<hr>
<h2 id="마치며">마치며</h2>
<p>테스트를 작성하다 보면 전체 테스트를 돌리기보다 특정 테스트만 반복 실행하는 경우가 많습니다. 오늘 다룬 옵션들을 활용하면 개발 중에 빠르게 피드백을 받으면서 테스트를 작성할 수 있습니다.</p>
<p>다음 글에서는 테스트 코드를 직접 작성하는 방법을 알아보겠습니다.</p>
<hr>
<blockquote>
<p>📚 <strong>참고 자료</strong></p>
<ul>
<li><a href="https://playwright.dev/docs/running-tests">Playwright - Running Tests</a></li>
<li><a href="https://playwright.dev/docs/test-cli">Playwright - Command Line</a></li>
</ul>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Playwright 설치 가이드]]></title>
            <link>https://velog.io/@00kang_jh/Playwright-%EC%84%A4%EC%B9%98-%EA%B0%80%EC%9D%B4%EB%93%9C</link>
            <guid>https://velog.io/@00kang_jh/Playwright-%EC%84%A4%EC%B9%98-%EA%B0%80%EC%9D%B4%EB%93%9C</guid>
            <pubDate>Thu, 22 Jan 2026 01:23:15 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>이전 글에서 Playwright가 무엇인지, 왜 사용하는지 알아봤습니다. 이번 글에서는 실제로 Playwright를 설치하고 첫 테스트를 실행하는 과정을 다뤄보겠습니다.</p>
<p>Node.js와 VS Code가 이미 설치되어 있다는 가정 하에 진행합니다.</p>
<blockquote>
<p>💻 <strong>작성 환경</strong></p>
<ul>
<li>macOS Tahoe 26.2</li>
<li>VS Code</li>
<li>Node.js</li>
</ul>
</blockquote>
<hr>
<h2 id="프로젝트-폴더-생성">프로젝트 폴더 생성</h2>
<p>먼저 Playwright 프로젝트를 위한 폴더를 생성하고 이동합니다.</p>
<pre><code class="language-bash">mkdir my-playwright-project
cd my-playwright-project</code></pre>
<p>VS Code에서 해당 폴더를 열어주세요.</p>
<pre><code class="language-bash">code .</code></pre>
<hr>
<h2 id="playwright-설치">Playwright 설치</h2>
<p>VS Code에서 터미널을 열고 다음 명령어를 실행합니다.</p>
<pre><code class="language-bash">npm init playwright@latest</code></pre>
<p>명령어를 실행하면 몇 가지 질문이 나타납니다.</p>
<h3 id="설치-옵션-선택">설치 옵션 선택</h3>
<p><img src="https://velog.velcdn.com/images/00kang_jh/post/182660a4-40c9-4610-a766-70d82bf4ef03/image.png" alt=""></p>
<p><strong>1. TypeScript 또는 JavaScript 선택</strong></p>
<pre><code>Do you want to use TypeScript or JavaScript?
  ▸ TypeScript
    JavaScript</code></pre><p>원하는 언어를 선택합니다. 이 가이드에서는 TypeScript를 기준으로 진행합니다.</p>
<p><strong>2. 테스트 폴더 경로</strong></p>
<pre><code>Where to put your end-to-end tests?
  ▸ tests</code></pre><p>테스트 파일이 저장될 폴더명입니다. 기본값인 <code>tests</code>를 그대로 사용하면 됩니다.</p>
<p><strong>3. GitHub Actions 워크플로우 추가 여부</strong></p>
<pre><code>Add a GitHub Actions workflow?
  ▸ false</code></pre><p>Github Actions에서 CI/CD 설정이 필요하면 <code>true</code>를 선택합니다. 지금은 <code>false</code>로 진행해도 괜찮습니다.</p>
<p><strong>4. 브라우저 설치 여부</strong></p>
<pre><code>Install Playwright browsers?
  ▸ true</code></pre><p>반드시 <code>true</code>를 선택합니다. Playwright가 사용할 브라우저 바이너리(Chromium, Firefox, WebKit)를 설치합니다. <code>false</code>를 할 경우 나중에 직접 브러우저를 설치해줘야하는 불편함이 있으니깐 웬만하면 <code>true</code>로 하시길 권장합니다.</p>
<hr>
<h2 id="설치-완료-확인">설치 완료 확인</h2>
<p>설치가 완료되면 프로젝트에 다음 파일들이 생성됩니다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/00kang_jh/post/c3968e95-ff2c-4525-b404-35ce7ed52d57/image.png" width="50%" />
</p>


<p><code>package.json</code>을 열어보면 Playwright가 devDependencies에 추가된 것을 확인할 수 있습니다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/00kang_jh/post/9dc0d368-cc88-4cab-b306-9bc2ae2b1970/image.png" width="50%" />
</p>


<hr>
<h2 id="제대로-설치되었는지-확인">제대로 설치되었는지 확인</h2>
<p>설치가 완료되면 다음 명령어로 Playwright 버전을 확인할 수 있습니다.</p>
<pre><code class="language-bash">npx playwright --version</code></pre>
<p>버전 정보가 출력되면 설치가 정상적으로 완료된 것입니다.
<img src="https://velog.velcdn.com/images/00kang_jh/post/066413b8-fe05-43e0-bc4e-77b5c061c692/image.png" alt=""></p>
<hr>
<h2 id="마치며">마치며</h2>
<p>터미널 명령어 몇 줄이면 Playwright 설치가 완료됩니다. 다음 글에서는 실제 테스트를 실행하고 코드를 작성하는 방법을 알아보겠습니다.</p>
<hr>
<blockquote>
<p>📚 <strong>참고 자료</strong></p>
<ul>
<li><a href="https://playwright.dev/docs/intro">Playwright - Installation</a></li>
<li><a href="https://playwright.dev/docs/running-tests">Playwright - Running Tests</a></li>
</ul>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Playwright, E2E 테스트 자동화 소개]]></title>
            <link>https://velog.io/@00kang_jh/Playwright-E2E-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%9E%90%EB%8F%99%ED%99%94-%EC%86%8C%EA%B0%9C</link>
            <guid>https://velog.io/@00kang_jh/Playwright-E2E-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%9E%90%EB%8F%99%ED%99%94-%EC%86%8C%EA%B0%9C</guid>
            <pubDate>Thu, 22 Jan 2026 00:52:26 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>웹 애플리케이션을 개발하다 보면 &quot;이 기능이 제대로 동작하는지 어떻게 확인하지?&quot;라는 고민을 하게 됩니다. 버튼을 클릭했을 때 페이지가 잘 이동하는지, 폼을 제출했을 때 데이터가 정상적으로 저장되는지, 매번 수동으로 확인하기엔 시간도 부족하고 실수할 가능성도 높죠.</p>
<p>이런 문제를 해결해주는 것이 바로 <strong>E2E(End-to-End) 테스트 자동화</strong>입니다. 그리고 오늘 소개할 <strong>Playwright</strong>는 이 영역에서 빠르게 주목받고 있는 도구입니다.</p>
<hr>
<h2 id="e2e-테스트란">E2E 테스트란?</h2>
<p>E2E 테스트는 사용자 관점에서 애플리케이션의 전체 흐름을 검증하는 테스트입니다.</p>
<p>예를 들어 쇼핑몰에서 &quot;로그인 → 상품 검색 → 장바구니 담기 → 결제&quot;까지의 과정이 정상적으로 동작하는지 확인하는 것이죠. 단위 테스트가 함수 하나하나를 검증한다면, E2E 테스트는 실제 사용자가 경험하는 시나리오 전체를 검증합니다.</p>
<p><strong>왜 필요할까요?</strong></p>
<ul>
<li>개별 컴포넌트는 정상이어도 통합했을 때 문제가 생길 수 있습니다</li>
<li>배포 전 주요 기능이 동작하는지 빠르게 확인할 수 있습니다</li>
<li>수동 테스트에 드는 반복적인 시간을 줄여줍니다</li>
</ul>
<hr>
<h2 id="playwright-소개">Playwright 소개</h2>
<p><strong>Playwright</strong>는 Microsoft에서 개발한 오픈소스 E2E 테스트 프레임워크입니다. 2020년에 처음 공개되었고, 빠른 속도와 안정성으로 Cypress, Selenium과 함께 대표적인 E2E 테스트 도구로 자리잡았습니다.</p>
<pre><code class="language-bash"># 설치
npm init playwright@latest</code></pre>
<p>설치 명령어 하나로 프로젝트 구조, 설정 파일, 예제 테스트까지 자동으로 생성해줍니다.</p>
<hr>
<h2 id="playwright의-주요-특징">Playwright의 주요 특징</h2>
<h3 id="1-크로스-브라우저-지원">1. 크로스 브라우저 지원</h3>
<p>Chromium, Firefox, WebKit 세 가지 브라우저 엔진을 단일 API로 제어할 수 있습니다. 하나의 테스트 코드로 Chrome, Safari, Firefox에서 모두 동작을 검증할 수 있다는 의미입니다.</p>
<pre><code class="language-javascript">// playwright.config.js
projects: [
  { name: &#39;chromium&#39;, use: { ...devices[&#39;Desktop Chrome&#39;] } },
  { name: &#39;firefox&#39;, use: { ...devices[&#39;Desktop Firefox&#39;] } },
  { name: &#39;webkit&#39;, use: { ...devices[&#39;Desktop Safari&#39;] } },
]</code></pre>
<h3 id="2-auto-waiting">2. Auto-waiting</h3>
<p>테스트 자동화에서 가장 골치 아픈 문제 중 하나가 타이밍입니다. 버튼이 아직 로딩 중인데 클릭하려고 하면 테스트가 실패하죠. Playwright는 요소가 클릭 가능한 상태가 될 때까지 자동으로 기다려줍니다.</p>
<pre><code class="language-javascript">// 별도의 wait 없이도 안정적으로 동작
await page.click(&#39;button#submit&#39;);</code></pre>
<h3 id="3-강력한-디버깅-도구">3. 강력한 디버깅 도구</h3>
<p>테스트가 실패했을 때 원인을 찾기 쉽도록 다양한 도구를 제공합니다.</p>
<ul>
<li><strong>Trace Viewer</strong>: 테스트 실행 과정을 스크린샷, DOM 스냅샷, 네트워크 요청과 함께 타임라인으로 확인</li>
<li><strong>Codegen</strong>: 브라우저에서 직접 조작하면 해당 동작을 코드로 자동 생성</li>
<li><strong>UI Mode</strong>: 테스트를 시각적으로 실행하고 각 단계를 확인</li>
</ul>
<pre><code class="language-bash"># Codegen 실행
npx playwright codegen https://example.com</code></pre>
<h3 id="4-네트워크-제어">4. 네트워크 제어</h3>
<p>API 요청을 가로채서 원하는 응답으로 대체하거나, 네트워크 상태를 시뮬레이션할 수 있습니다. 백엔드 없이도 프론트엔드 테스트가 가능해집니다.</p>
<pre><code class="language-javascript">await page.route(&#39;**/api/users&#39;, route =&gt; {
  route.fulfill({
    status: 200,
    body: JSON.stringify([{ id: 1, name: &#39;Test User&#39; }])
  });
});</code></pre>
<h3 id="5-병렬-실행">5. 병렬 실행</h3>
<p>테스트를 여러 워커에서 동시에 실행하여 전체 테스트 시간을 크게 단축할 수 있습니다.</p>
<hr>
<h2 id="playwright의-장점">Playwright의 장점</h2>
<h3 id="빠른-실행-속도">빠른 실행 속도</h3>
<p>브라우저와 직접 통신하는 방식을 사용해 Selenium 대비 체감될 정도로 빠릅니다. 특히 병렬 실행을 기본 지원하기 때문에 테스트 수가 많아져도 전체 실행 시간을 효율적으로 관리할 수 있습니다.</p>
<h3 id="안정적인-테스트">안정적인 테스트</h3>
<p>Auto-waiting과 자동 재시도(retry) 기능 덕분에 &quot;어제는 됐는데 오늘은 안 되는&quot; 불안정한 테스트(flaky test)가 줄어듭니다. 이건 실무에서 정말 큰 장점입니다.</p>
<h3 id="낮은-진입-장벽">낮은 진입 장벽</h3>
<p>Codegen으로 브라우저 조작을 녹화해서 코드를 생성할 수 있어, 처음 접하는 분들도 빠르게 테스트를 작성할 수 있습니다. 공식 문서도 잘 정리되어 있고 예제가 풍부합니다.</p>
<h3 id="활발한-생태계">활발한 생태계</h3>
<p>Microsoft의 지원 아래 꾸준히 업데이트되고 있고, VS Code 확장 프로그램도 제공합니다. GitHub Actions 등 CI/CD 파이프라인과의 연동도 간편합니다.</p>
<hr>
<h2 id="마치며">마치며</h2>
<p>E2E 테스트 자동화를 시작하려는 분들에게 Playwright는 좋은 선택지입니다. 설정이 간단하고, 디버깅 도구가 강력하며, 무엇보다 안정적으로 동작합니다.</p>
<p>다음 글에서는 실제로 간단한 테스트를 작성하고 실행하는 과정을 다뤄보겠습니다.</p>
<hr>
<blockquote>
<p>📚 <strong>참고 자료</strong></p>
<ul>
<li><a href="https://playwright.dev/">Playwright 공식 문서</a></li>
<li><a href="https://github.com/microsoft/playwright">Playwright GitHub</a></li>
</ul>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[🚀 AWS S3 이미지 업로드]]></title>
            <link>https://velog.io/@00kang_jh/AWS-S3-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%97%85%EB%A1%9C%EB%93%9C</link>
            <guid>https://velog.io/@00kang_jh/AWS-S3-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%97%85%EB%A1%9C%EB%93%9C</guid>
            <pubDate>Sat, 02 Aug 2025 06:07:02 GMT</pubDate>
            <description><![CDATA[<h1 id="🚀-aws-s3-이미지-업로드-구현-가이드">🚀 AWS S3 이미지 업로드 구현 가이드</h1>
<p>일반적으로 많은 프로젝트들에서 기본적으로 이미지 업로드 하는 기능들이 많이 들어갑니다.
그래서  AWS 기반으로 <strong>S3 + CloudFront + Presigned URL</strong> 조합을 사용하면 빠르고
안전하며 비용 효율적인 이미지 업로드를 구현할 수 있습니다.</p>
<hr>
<h2 id="1️⃣-이미지-업로드-아키텍처">1️⃣ 이미지 업로드 아키텍처</h2>
<p><strong>흐름</strong>  </p>
<ol>
<li>클라이언트 → 서버: 업로드 요청</li>
<li>서버 → AWS: Presigned URL 생성</li>
<li>서버 → 클라이언트: Presigned URL 전달</li>
<li>클라이언트 → S3: 이미지 직접 업로드</li>
<li>서버 → DB: 업로드 정보(경로, 파일명, 유저) 저장</li>
<li>조회 시: <strong>CloudFront URL</strong> 통해 이미지 접근</li>
</ol>
<blockquote>
<p>✅ 장점: 서버 부하 최소화 + 빠른 CDN 조회 + 보안 강화</p>
</blockquote>
<hr>
<h2 id="2️⃣-spring-boot-구현">2️⃣ Spring Boot 구현</h2>
<h3 id="🔹-presigned-url-생성">🔹 Presigned URL 생성</h3>
<pre><code class="language-java">@Service
@RequiredArgsConstructor
public class S3Service {
    private final AmazonS3 amazonS3;
    private final String bucketName = &quot;demo-images&quot;;

    public String generatePresignedUrl(String fileName) {
        Date expiration = new Date(System.currentTimeMillis() + 1000 * 60 * 5); // 5분 유효
        GeneratePresignedUrlRequest request =
            new GeneratePresignedUrlRequest(bucketName, fileName)
            .withMethod(HttpMethod.PUT)
            .withExpiration(expiration);
        return amazonS3.generatePresignedUrl(request).toString();
    }
}</code></pre>
<h3 id="🔹-클라이언트-업로드-react-native-예시">🔹 클라이언트 업로드 (React Native 예시)</h3>
<pre><code class="language-javascript">await axios.put(presignedUrl, file, {
  headers: { &quot;Content-Type&quot;: file.type }
});</code></pre>
<hr>
<h2 id="3️⃣-cloudfront--s3-구조">3️⃣ CloudFront + S3 구조</h2>
<ul>
<li><strong>S3 (Private)</strong>: 이미지 저장</li>
<li><strong>CloudFront</strong>: 전 세계 CDN 캐싱</li>
<li><strong>OAC(Origin Access Control)</strong>: S3 직접 접근 차단</li>
<li><strong>Signed URL(Optional)</strong>: 민감한 이미지 접근 제어
<img src="blob:https://velog.io/8a7be4c6-642e-494b-bf20-0186ec45d62e" alt="업로드중.."></li>
</ul>
<p><em>(예시 다이어그램: S3 + CloudFront + Presigned URL 구조)</em></p>
<hr>
<h2 id="4️⃣-보안-체크리스트">4️⃣ 보안 체크리스트</h2>
<p>업로드 서비스는 <strong>XSS·파일 위장·대용량 업로드</strong>에 취약할 수 있습니다.<br>아래 보안 전략을 적용하면 안전합니다.</p>
<ul>
<li><input disabled="" type="checkbox"> 허용 확장자만 업로드 (<code>jpg, png, webp</code>)</li>
<li><input disabled="" type="checkbox"> MIME 타입 검증 (<code>image/</code> 로 시작 여부)</li>
<li><input disabled="" type="checkbox"> 파일 크기 제한 (1~2MB)</li>
<li><input disabled="" type="checkbox"> UUID 기반 파일명 (사용자 입력 이름 사용 ❌)</li>
<li><input disabled="" type="checkbox"> S3 Private + Presigned URL 사용</li>
<li><input disabled="" type="checkbox"> CloudFront OAC 설정으로 직접 접근 차단</li>
<li><input disabled="" type="checkbox"> 필요 시 Signed URL or JWT 인증</li>
<li><input disabled="" type="checkbox"> 업로드 후 Lambda로 이미지 포맷 검증</li>
</ul>
<hr>
<h2 id="5️⃣-비용-최적화">5️⃣ 비용 최적화</h2>
<ul>
<li><strong>프리티어 사용 시</strong>  <ul>
<li>S3 5GB / CloudFront 1TB / 20,000 GET / 2,000 PUT 무료</li>
</ul>
</li>
<li><strong>월 사용량 예시 (300명 × 5장/일 × 1MB)</strong>  <ul>
<li>45GB 저장, 45,000 PUT → 월 $2~3 수준</li>
</ul>
</li>
<li><strong>비용 절감 방법</strong>  <ul>
<li>업로드 전 <strong>리사이징/압축</strong>  </li>
<li>오래된 파일 <strong>S3 Glacier로 이동</strong></li>
</ul>
</li>
</ul>
<hr>
<h2 id="6️⃣-최종-정리">6️⃣ 최종 정리</h2>
<blockquote>
<p><strong>S3 + CloudFront + Presigned URL</strong><br>구조를 적용하면 서버 부하를 최소화하고,<br>보안·속도·비용 모든 면에서 효율적인 구조를 만들 수 있습니다.</p>
</blockquote>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[미션 DAY4 (워밍업 클럽 3기)]]></title>
            <link>https://velog.io/@00kang_jh/%EB%AF%B8%EC%85%98-DAY4-%EC%9B%8C%EB%B0%8D%EC%97%85-%ED%81%B4%EB%9F%BD-3%EA%B8%B0</link>
            <guid>https://velog.io/@00kang_jh/%EB%AF%B8%EC%85%98-DAY4-%EC%9B%8C%EB%B0%8D%EC%97%85-%ED%81%B4%EB%9F%BD-3%EA%B8%B0</guid>
            <pubDate>Fri, 07 Mar 2025 12:29:57 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-day-4-코드-리팩토링--solid-원칙-정리">📌 Day 4: 코드 리팩토링 &amp; SOLID 원칙 정리</h1>
<p>이번 미션에서는 <strong>더 읽기 좋은 코드</strong>를 만들기 위해 기존 코드를 리팩토링하고, <strong>SOLID 원칙</strong>을 자기만의 언어로 정리하는 작업을 수행했다.  </p>
<hr>
<h2 id="✅-미션-수행-목표">✅ <strong>미션 수행 목표</strong></h2>
<h3 id="🔹-1-코드-리팩토링">🔹 1. 코드 리팩토링</h3>
<ul>
<li>[섹션 3. 논리, 사고의 흐름]에서 이야기하는 내용을 중심으로 리팩토링  </li>
<li>코드의 가독성을 높이고, 유지보수를 쉽게 만들기  </li>
</ul>
<h3 id="🔹-2-solid-원칙-정리">🔹 2. SOLID 원칙 정리</h3>
<ul>
<li>SOLID 원칙을 이해하고, 이를 나만의 언어로 정리  </li>
</ul>
<hr>
<h1 id="📌-1️⃣-코드-리팩토링-과정">📌 1️⃣ 코드 리팩토링 과정</h1>
<h3 id="🕒-step-1-기존-코드-분석">🕒 <strong>[Step 1] 기존 코드 분석</strong></h3>
<p>다음은 주어진 <code>validateOrder</code> 메서드의 기존 코드이다.  </p>
<p>📌 <strong>기존 코드</strong>  </p>
<pre><code class="language-java">public boolean validateOrder(Order order) {
    if (order.getItems().size() == 0) {
        log.info(&quot;주문 항목이 없습니다.&quot;);
        return false;
    } else {
        if (order.getTotalPrice() &gt; 0) {
            if (!order.hasCustomerInfo()) {
                log.info(&quot;사용자 정보가 없습니다.&quot;);
                return false;
            } else {
                return true;
            }
        } else if (!(order.getTotalPrice() &gt; 0)) {
            log.info(&quot;올바르지 않은 총 가격입니다.&quot;);
            return false;
        }
    }
    return true;
}</code></pre>
<p>🔍 <strong>분석 결과</strong>  </p>
<ul>
<li><strong><code>getter</code>를 직접 호출하는 부분이 많음</strong> → 캡슐화를 해치고 있음  </li>
<li><strong>부정 연산자(<code>!</code>, <code>!=</code>) 사용이 많음</strong> → 코드의 직관성이 떨어짐  </li>
<li><strong>중첩된 조건문이 많음</strong> → 가독성이 낮음  </li>
</ul>
<hr>
<h3 id="🕒-step-2-리팩토링-방향-설정">🕒 <strong>[Step 2] 리팩토링 방향 설정</strong></h3>
<p>리팩토링 시 고려할 사항:<br>✔️ <code>getter</code> 사용을 최소화하기 위해 <strong>Order 내부에 메서드를 추가</strong><br>✔️ <code>부정 연산자(!)</code>를 제거하여 가독성을 높임<br>✔️ <strong>불필요한 else 문을 제거</strong>하여 코드 흐름을 단순화  </p>
<hr>
<h3 id="🕒-step-3-order-클래스-개선">🕒 <strong>[Step 3] Order 클래스 개선</strong></h3>
<p><code>Order</code> 클래스 내부에 필요한 메서드를 추가하여 <strong>객체 스스로 판단할 수 있도록 수정</strong>  </p>
<p>📌 <strong>수정된 Order 클래스</strong>  </p>
<pre><code class="language-java">import java.util.List;

public class Order {

  private final List&lt;Integer&gt; items;
  private final int totalPrice;
  private final int customer;

  public Order(List&lt;Integer&gt; items, int totalPrice, int customer) {
    this.items = items;
    this.totalPrice = totalPrice;
    this.customer = customer;
  }

  public boolean isEmpty() {
    return items.isEmpty();
  }

  public boolean hasCustomerInfo() {
    return customer != 0;
  }

  public boolean moreThanZeroTotalPrice() {
    return totalPrice &gt; 0;
  }
}
</code></pre>
<hr>
<h3 id="🕒-step-4-리팩토링된-validateorder-메서드">🕒 <strong>[Step 4] 리팩토링된 <code>validateOrder</code> 메서드</strong></h3>
<p>리팩토링된 <code>validateOrder</code>는 <strong>더 직관적이고 읽기 쉬운 코드</strong>가 되었다.  </p>
<p>📌 <strong>수정된 validateOrder 메서드</strong>  </p>
<pre><code class="language-java">public class MissionDay4 {

  public boolean validateOrder(Order order) {
    if (order.isEmpty()) {
      log.info(&quot;주문 항목이 없습니다.&quot;);
      return false;
    }

    if (order.moreThanZeroTotalPrice()) {
      if (order.hasCustomerInfo()) {
        return true;
      }
      log.info(&quot;사용자 정보가 없습니다.&quot;);
      return false;
    }

    log.info(&quot;올바르지 않은 총 가격입니다.&quot;);
    return false;
  }
}

</code></pre>
<hr>
<h2 id="🔍-리팩토링-결과-정리">🔍 <strong>리팩토링 결과 정리</strong></h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>리팩토링 전</th>
<th>리팩토링 후</th>
</tr>
</thead>
<tbody><tr>
<td><strong>getter 사용</strong></td>
<td>직접 호출 (<code>order.getTotalPrice()</code>)</td>
<td>Order 내부에서 메서드 제공 (<code>order.moreThanZeroTotalPrice()</code>)</td>
</tr>
<tr>
<td><strong>부정 연산자</strong></td>
<td><code>!order.hasCustomerInfo()</code> 사용</td>
<td><code>order.hasCustomerInfo()</code>로 긍정 표현</td>
</tr>
<tr>
<td><strong>조건문 구조</strong></td>
<td>중첩된 <code>if-else</code> 다수 포함</td>
<td>단순한 <code>if</code> 구조로 개선</td>
</tr>
<tr>
<td><strong>가독성</strong></td>
<td>흐름 파악이 어려움</td>
<td>더 직관적인 코드</td>
</tr>
</tbody></table>
<hr>
<h1 id="📌-2️⃣-solid-원칙-정리">📌 2️⃣ SOLID 원칙 정리</h1>
<p><strong>SOLID 원칙</strong>은 객체 지향 프로그래밍에서 <strong>유지보수성과 확장성을 높이기 위한 설계 원칙</strong>이다.<br>아래는 내가 이해한 SOLID 원칙을 나만의 언어로 정리한 내용이다.  </p>
<h3 id="✅-1-단일-책임-원칙-srp-single-responsibility-principle">✅ <strong>1. 단일 책임 원칙 (SRP, Single Responsibility Principle)</strong></h3>
<p><strong>&quot;하나의 클래스는 하나의 책임만 가져야 한다.&quot;</strong>  </p>
<ul>
<li>한 클래스가 너무 많은 역할을 맡으면 <strong>변경이 어려워짐</strong>  </li>
<li>하나의 책임만 있도록 <strong>기능을 나누는 것이 중요</strong>  </li>
</ul>
<h3 id="✅-2-개방-폐쇄-원칙-ocp-openclosed-principle">✅ <strong>2. 개방-폐쇄 원칙 (OCP, Open/Closed Principle)</strong></h3>
<p><strong>&quot;확장은 가능하지만, 기존 코드는 수정하지 않는다.&quot;</strong>  </p>
<ul>
<li>새로운 기능이 필요하면 <strong>기존 코드를 수정하는 것이 아니라, 확장해야 함</strong>  </li>
<li>인터페이스, 추상 클래스를 활용하면 유연한 코드 작성 가능  </li>
</ul>
<h3 id="✅-3-리스코프-치환-원칙-lsp-liskov-substitution-principle">✅ <strong>3. 리스코프 치환 원칙 (LSP, Liskov Substitution Principle)</strong></h3>
<p><strong>&quot;하위 클래스는 상위 클래스를 대체할 수 있어야 한다.&quot;</strong>  </p>
<ul>
<li>부모 클래스를 상속받은 자식 클래스가 <strong>부모처럼 동작해야 함</strong>  </li>
<li>그렇지 않으면 다형성을 활용할 수 없음  </li>
</ul>
<h3 id="✅-4-인터페이스-분리-원칙-isp-interface-segregation-principle">✅ <strong>4. 인터페이스 분리 원칙 (ISP, Interface Segregation Principle)</strong></h3>
<p><strong>&quot;필요한 기능만 포함된 인터페이스를 사용하라.&quot;</strong>  </p>
<ul>
<li>한 개의 큰 인터페이스보다는, <strong>작은 단위의 인터페이스를 여러 개 만드는 것이 좋음</strong>  </li>
<li>필요하지 않은 기능까지 구현하지 않아도 됨  </li>
</ul>
<h3 id="✅-5-의존-역전-원칙-dip-dependency-inversion-principle">✅ <strong>5. 의존 역전 원칙 (DIP, Dependency Inversion Principle)</strong></h3>
<p><strong>&quot;구체적인 것보다, 추상적인 것에 의존하라.&quot;</strong>  </p>
<ul>
<li>클래스가 다른 클래스를 직접 참조하는 것이 아니라, <strong>인터페이스나 추상 클래스를 통해 의존</strong>  </li>
<li>변경에 유연한 구조를 만들 수 있음  </li>
</ul>
<hr>
<h2 id="📌-출처">📌 출처</h2>
<p>📖 <strong>강의 제목:</strong> <a href="https://www.inflearn.com/course/readable-code-%EC%9D%BD%EA%B8%B0%EC%A2%8B%EC%9D%80%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1%EC%82%AC%EA%B3%A0%EB%B2%95">Readable Code: 읽기 좋은 코드를 작성하는 사고법</a><br>👩‍🏫 <strong>강사:</strong> 박우빈  </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SQLD 공부 (2)]]></title>
            <link>https://velog.io/@00kang_jh/SQLD-%EA%B3%B5%EB%B6%80-2</link>
            <guid>https://velog.io/@00kang_jh/SQLD-%EA%B3%B5%EB%B6%80-2</guid>
            <pubDate>Tue, 04 Mar 2025 06:53:08 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-entity-attribute-instance-개념-정리">📌 Entity, Attribute, Instance 개념 정리</h1>
<p>데이터 모델링에서 <strong>Entity(개체), Attribute(속성), Instance(인스턴스)</strong> 개념을 이해하는 것이 중요하다. 이를 쉽게 설명하기 위해 간단한 예제와 함께 살펴보자.  </p>
<hr>
<h2 id="🔹-개념-정리">🔹 개념 정리</h2>
<ul>
<li><strong>Entity (개체):</strong> 관리할 대상이 되는 데이터 (예: 학생, 강의, 도서 등)  </li>
<li><strong>Attribute (속성):</strong> 개체가 가지는 정보 (예: 학번, 이름, 학년 등)  </li>
<li><strong>Instance (인스턴스):</strong> 개체(Entity)의 실제 데이터 값 (예: 학번 <code>2023001</code>, 이름 <code>김철수</code>)  </li>
</ul>
<blockquote>
<p><strong>Entity는 데이터를 저장하는 개체, Attribute는 개체가 가지는 속성, Instance는 실제 데이터를 의미한다.</strong></p>
</blockquote>
<hr>
<h2 id="🎯-이해하기-쉬운-비유">🎯 이해하기 쉬운 비유</h2>
<p>📦 <strong>택배 시스템으로 설명하면?</strong>  </p>
<ul>
<li><strong>Entity (개체) → &quot;택배 상자&quot;</strong>  </li>
<li><strong>Attribute (속성) → &quot;택배 상자에 붙어 있는 송장 (수령인, 주소, 무게)&quot;</strong>  </li>
<li><strong>Instance (인스턴스) → &quot;실제 배송 중인 특정 택배&quot;</strong>  </li>
</ul>
<p>이렇게 비유하면 <strong>Entity, Attribute, Instance</strong> 개념이 더 쉽게 이해될 것이다! 🚀  </p>
<hr>
<h1 id="🔹-entity-엔터티">🔹 Entity (엔터티)</h1>
<blockquote>
<p><strong>&quot;식별 가능한 객체&quot;</strong>  </p>
</blockquote>
<h3 id="✅-엔터티의-특징">✅ 엔터티의 특징</h3>
<ol>
<li><strong>업무에서 사용되는 정보여야 함</strong>  </li>
<li><strong>유일성을 보장하는 식별자(Primary Key)가 있어야 함</strong>  </li>
<li><strong>두 개 이상의 인스턴스(Row)를 가져야 함</strong>  </li>
<li><strong>반드시 속성(Attribute)을 포함해야 함</strong>  </li>
<li><strong>다른 엔터티와 하나 이상의 관계(Relationship)를 가져야 함</strong>  </li>
</ol>
<hr>
<h2 id="🔹-엔터티의-분류">🔹 엔터티의 분류</h2>
<h3 id="1️⃣-유형에-따른-분류">1️⃣ <strong>유형에 따른 분류</strong></h3>
<ul>
<li><strong>유형(Entity):</strong> 물리적인 형태가 존재 (예: <code>학생</code>, <code>도서</code>)  </li>
<li><strong>개념(Entity):</strong> 물리적 형태 없이 개념적으로 존재 (예: <code>강의 계획</code>)  </li>
<li><strong>사건(Entity):</strong> 행위를 통해 발생하며, 빈번하고 통계 자료로 활용 가능 (예: <code>주문 내역</code>)  </li>
</ul>
<h3 id="2️⃣-발생-시점에-따른-분류">2️⃣ <strong>발생 시점에 따른 분류</strong></h3>
<ul>
<li><strong>기본 엔터티:</strong> 원래 존재하는 독립적인 정보, 자식 엔터티를 가질 수 있음  </li>
<li><strong>중심 엔터티:</strong> 기본 엔터티에서 파생되며, 데이터 양이 많고 업무에서 중심적인 역할 수행  </li>
<li><strong>행위 엔터티:</strong> 두 개 이상의 엔터티에서 파생되며, 데이터의 수정과 증가가 잦음  </li>
</ul>
<hr>
<h1 id="🔹-attribute-속성">🔹 Attribute (속성)</h1>
<blockquote>
<p><strong>&quot;객체의 특징&quot;</strong>  </p>
</blockquote>
<p>예를 들어, &quot;사람&quot;이라는 객체가 있다면 <strong>이름, 나이, 생년월일</strong> 등 다양한 특징이 존재할 것이다. 이처럼 개체(Entity)의 특정 정보를 나타내는 요소를 <strong>속성(Attribute)</strong>이라고 한다.  </p>
<hr>
<h2 id="🔹-속성의-분류">🔹 속성의 분류</h2>
<h3 id="1️⃣-특성에-따른-분류">1️⃣ <strong>특성에 따른 분류</strong></h3>
<ul>
<li><strong>기본 속성 (Basic Attribute):</strong> 업무 프로세스에서 바로 정의할 수 있는 속성  </li>
<li><strong>설계 속성 (Designed Attribute):</strong> 업무에 존재하지 않지만 설계 시 필요하다고 판단되는 속성 (예: <code>고유번호</code>)  </li>
<li><strong>파생 속성 (Derived Attribute):</strong> 다른 속성의 값을 계산하거나 특정 규칙으로 변형하여 생성하는 속성  </li>
</ul>
<h3 id="2️⃣-구성-방식에-따른-분류">2️⃣ <strong>구성 방식에 따른 분류</strong></h3>
<ul>
<li><strong>PK (Primary Key) 속성:</strong> 엔터티의 인스턴스를 식별할 수 있는 속성  </li>
<li><strong>FK (Foreign Key) 속성:</strong> 다른 엔터티의 속성에서 가져온 속성  </li>
<li><strong>일반 속성:</strong> PK, FK를 제외한 나머지 속성  </li>
</ul>
<hr>
<h2 id="🔹-도메인-domain">🔹 도메인 (Domain)</h2>
<blockquote>
<p><strong>&quot;속성이 가질 수 있는 값의 범위&quot;</strong>  </p>
</blockquote>
<p>예를 들어, <code>성별</code> 속성의 도메인은 <strong>[남, 여]</strong> 와 같이 미리 정의된 값의 범위를 가진다.  </p>
<hr>
<p>📖 <strong>참고 도서:</strong> 《SQL개발자 SQLD 과외노트》 - 정미나, 시대에듀  </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SQLD 공부 (1)]]></title>
            <link>https://velog.io/@00kang_jh/SQLD-%EA%B3%B5%EB%B6%80-1</link>
            <guid>https://velog.io/@00kang_jh/SQLD-%EA%B3%B5%EB%B6%80-1</guid>
            <pubDate>Sat, 01 Mar 2025 02:02:23 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-데이터-모델의-이해">📌 데이터 모델의 이해</h1>
<h2 id="🔹-모델링modeling이란">🔹 모델링(Modeling)이란?</h2>
<blockquote>
<p><strong>&quot;현실 세계를 단순화하여 표현하는 기법&quot;</strong></p>
</blockquote>
<h3 id="✅-모델링의-3가지-조건">✅ 모델링의 3가지 조건</h3>
<ol>
<li><strong>현실 세계를 반영해야 함</strong>  </li>
<li><strong>단순화하여 표현해야 함</strong>  </li>
<li><strong>관찰하고자 하는 데이터를 모델로 설계해야 함</strong>  </li>
</ol>
<hr>
<h2 id="🔹-모델링의-3가지-특징">🔹 모델링의 3가지 특징</h2>
<h3 id="1️⃣-추상화abstraction">1️⃣ <strong>추상화(Abstraction)</strong></h3>
<ul>
<li>현실 세계를 일정한 형식으로 표현하는 과정  </li>
<li>아이디어나 개념을 간략하게 정리  </li>
</ul>
<h3 id="2️⃣-단순화simplification">2️⃣ <strong>단순화(Simplification)</strong></h3>
<ul>
<li>복잡한 현실 세계를 정해진 표기법으로 쉽게 표현  </li>
</ul>
<h3 id="3️⃣-명확화clarity">3️⃣ <strong>명확화(Clarity)</strong></h3>
<ul>
<li>불분명한 요소를 제거하여 해석이 명확하게 가능하도록 표현  </li>
</ul>
<hr>
<h2 id="🔹-모델링의-3가지-관점">🔹 모델링의 3가지 관점</h2>
<h3 id="1️⃣-데이터-관점-data-perspective">1️⃣ <strong>데이터 관점 (Data Perspective)</strong></h3>
<ul>
<li><strong>데이터 중심</strong>의 모델링  </li>
<li>업무에 연관된 데이터와 <strong>데이터 간의 관계</strong>를 분석  </li>
</ul>
<h3 id="2️⃣-프로세스-관점-process-perspective">2️⃣ <strong>프로세스 관점 (Process Perspective)</strong></h3>
<ul>
<li><strong>업무 프로세스 중심</strong>의 모델링  </li>
<li>현재 또는 미래의 업무 처리 방식에 초점  </li>
</ul>
<h3 id="3️⃣-데이터와-프로세스의-상관-관점-interaction-perspective">3️⃣ <strong>데이터와 프로세스의 상관 관점 (Interaction Perspective)</strong></h3>
<ul>
<li><strong>데이터와 프로세스의 관계</strong>를 분석  </li>
<li>프로세스의 흐름에 따라 데이터가 어떻게 변하는지 모델링  </li>
</ul>
<hr>
<h2 id="🔹-모델링의-3가지-단계">🔹 모델링의 3가지 단계</h2>
<h3 id="1️⃣-개념적-데이터-모델링-conceptual-data-modeling">1️⃣ 개념적 데이터 모델링 (Conceptual Data Modeling)</h3>
<ul>
<li><strong>가장 추상화 수준이 높은 모델링 단계</strong>  </li>
<li>기업(전사) 차원의 데이터를 분석하고, <strong>업무 중심적이고 포괄적인 모델링</strong>을 수행  </li>
<li>개체(Entity)와 그 관계(Relationship)를 정의하는 단계  </li>
</ul>
<h3 id="2️⃣-논리적-데이터-모델링-logical-data-modeling">2️⃣ 논리적 데이터 모델링 (Logical Data Modeling)</h3>
<ul>
<li><strong>데이터베이스를 설계하기 위한 핵심 모델링 단계</strong>  </li>
<li><strong>재사용성이 가장 높은 모델링</strong>으로,  <ul>
<li>Key(키), 속성(Attribute), 관계(Relationship) 등을 상세하게 정의  </li>
</ul>
</li>
<li>DBMS에 <strong>독립적인</strong> 설계가 가능  </li>
</ul>
<h3 id="3️⃣-물리적-데이터-모델링-physical-data-modeling">3️⃣ 물리적 데이터 모델링 (Physical Data Modeling)</h3>
<ul>
<li><strong>실제 데이터베이스로 구현할 수 있도록 성능과 가용성을 고려하는 단계</strong>  </li>
<li>데이터의 <strong>물리적 저장 구조(테이블, 컬럼, 인덱스 등)</strong>를 정의  </li>
<li>DBMS의 특성을 반영하여 최적화 진행  </li>
</ul>
<hr>
<h2 id="🔹-데이터의-독립성-data-independence">🔹 데이터의 독립성 (Data Independence)</h2>
<blockquote>
<p><strong>&quot;ANSI-SPARC 아키텍처는 DBMS의 추상적인 설계 표준이다.&quot;</strong><br>✅ <strong>총 3단계의 스키마 구조를 갖고 있음</strong>  </p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/00kang_jh/post/17e29daf-7924-4eb2-b65c-6a5ee2f38b13/image.png" alt="ANSI-SPARC 아키텍처"></p>
<h3 id="1️⃣-외부-스키마-external-schema">1️⃣ 외부 스키마 (External Schema)</h3>
<ul>
<li><strong>사용자 관점 (User View)</strong>  </li>
<li>사용자가 데이터베이스를 보는 관점에 따라 <strong>각기 다른 스키마</strong>를 정의  </li>
<li>여러 개의 외부 스키마가 존재할 수 있음 (Multiple User&#39;s View)  </li>
</ul>
<h3 id="2️⃣-개념-스키마-conceptual-schema">2️⃣ 개념 스키마 (Conceptual Schema)</h3>
<ul>
<li><strong>통합된 관점 (Community View of DB)</strong>  </li>
<li>데이터베이스 전체 구조를 나타내는 스키마  </li>
<li><strong>모든 사용자</strong>가 공유하는 <strong>데이터의 구조 및 관계</strong>를 정의  </li>
</ul>
<h3 id="3️⃣-내부-스키마-internal-schema">3️⃣ 내부 스키마 (Internal Schema)</h3>
<ul>
<li><strong>물리적인 관점 (Physical Representation)</strong>  </li>
<li>데이터의 <strong>저장 방식, 인덱스, 컬럼 정의</strong> 등을 포함  </li>
<li>실제 물리적인 저장 구조를 나타냄  </li>
</ul>
<h3 id="🔸-데이터-독립성이-보장하는-2가지-원칙">🔸 데이터 독립성이 보장하는 2가지 원칙</h3>
<ol>
<li><strong>논리적 독립성 (Logical Independence)</strong>  <ul>
<li><strong>개념 스키마</strong>가 변경되어도 <strong>외부 스키마(사용자 관점)는 영향을 받지 않음</strong>  </li>
</ul>
</li>
<li><strong>물리적 독립성 (Physical Independence)</strong>  <ul>
<li><strong>내부 스키마</strong>(저장 구조)가 변경되어도 <strong>외부/개념 스키마는 영향을 받지 않음</strong>  </li>
</ul>
</li>
</ol>
<hr>
<h2 id="🔹-erd-entity-relationship-diagram">🔹 ERD (Entity Relationship Diagram)</h2>
<blockquote>
<p><strong>&quot;어떤 개체(Entity)들이 존재하고, 그들 간에 어떤 관계(Relationship)가 있는지를 시각적으로 표현한 다이어그램&quot;</strong></p>
</blockquote>
<p>✅ <strong>ERD의 주요 구성 요소</strong></p>
<ul>
<li><strong>Entity(개체):</strong> 데이터를 저장하는 주요 단위 (예: 고객, 주문, 제품)  </li>
<li><strong>Attribute(속성):</strong> 개체가 가지는 정보 (예: 고객 이름, 주문 날짜)  </li>
<li><strong>Relationship(관계):</strong> 개체 간의 연관성 (예: 고객은 주문을 한다)  </li>
</ul>
<p>📝 <strong>이 단계에서는 개념을 큰 틀에서 이해하는 것이 중요함!</strong>  </p>
<hr>
<p>📖 <strong>참고 도서:</strong> 《SQL개발자 SQLD 과외노트》 - 정미나, 시대에듀 </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CodingTest_Day1_Array]]></title>
            <link>https://velog.io/@00kang_jh/CodingTestDay1Array</link>
            <guid>https://velog.io/@00kang_jh/CodingTestDay1Array</guid>
            <pubDate>Thu, 02 Jan 2025 14:12:23 GMT</pubDate>
            <description><![CDATA[<p>이번 주제는 Array 배열입니다. 우리는 이미 잘 알고 있기에 복습한다고 생각하시면 좋을 것 같습니다.</p>
<hr>
<h2 id="array배열의-기본-개념">Array(배열)의 기본 개념</h2>
<h4 id="배열이란">배열이란?</h4>
<p><strong>&quot;동일한 타입의 데이터를 연속된 메모리 공간에 저장하는 자료구조&quot;</strong> 라고 정의할 수 있습니다.</p>
<h4 id="배열의-장점과-단점">배열의 장점과 단점</h4>
<p>장점 : 빠른 접근 속도 (<strong>O(1)</strong>로 인덱스 접근이 가능합니다.)
단점 : 크기가 고정되어 있으며, 삽입 or 삭제 시 성능이 저하됩니다.</p>
<hr>
<h2 id="array배열의-연산">Array(배열)의 연산</h2>
<h4 id="배열-생성-및-초기화">배열 생성 및 초기화</h4>
<pre><code class="language-java">int[] arr = new int[5]; // 크기가 5인 배열 생성
int[] arr2 = {1, 2, 3, 4, 5}; // 초기화된 배열</code></pre>
<h4 id="요소-접근-및-수정">요소 접근 및 수정</h4>
<pre><code class="language-java">arr[0] = 10; // 배열 첫 번째 요소를 10으로 수정
System.out.println(arr[0]); // 출력: 10</code></pre>
<h4 id="다차원-배열">다차원 배열</h4>
<pre><code class="language-java">int[][] matrix = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};
System.out.println(matrix[0][1]); // 출력: 2</code></pre>
<h4 id="가변-배열-jagged-array">가변 배열 (Jagged Array)</h4>
<pre><code class="language-java">int[][] jagged = new int[3][];
jagged[0] = new int[2]; // 첫 번째 행의 길이: 2
jagged[1] = new int[3]; // 두 번째 행의 길이: 3
jagged[2] = new int[1]; // 세 번째 행의 길이: 1</code></pre>
<hr>
<h2 id="array배열의-알고리즘적-활용">Array(배열)의 알고리즘적 활용</h2>
<h4 id="정렬오름차순내림차순">정렬(오름차순/내림차순)</h4>
<pre><code class="language-java">Arrays.sort(arr); // 오름차순 정렬
Arrays.sort(arr, Collections.reverseOrder()); // 내림차순 정렬 (Integer[] 필요)</code></pre>
<h4 id="탐색linear-search-binary-search-최대값최소값-찾기">탐색(Linear Search, Binary Search, 최대값/최소값 찾기)</h4>
<pre><code class="language-java">//선형 탐색(Linear Search)
int key = 3;
for (int i = 0; i &lt; arr.length; i++) {
    if (arr[i] == key) {
        System.out.println(&quot;Found at index: &quot; + i);
    }
}</code></pre>
<pre><code class="language-java">//이진 탐색 (Binary Search)
int index = Arrays.binarySearch(arr, key); // 정렬된 배열에 사용
System.out.println(&quot;Index: &quot; + index);
</code></pre>
<pre><code class="language-java">//최대값 찾기, 최소값은 if문만 변경해서 작성하면 됨.
int max = arr[0];
for (int i = 1; i &lt; arr.length; i++) {
    if (arr[i] &gt; max) max = arr[i];
}
System.out.println(&quot;Max: &quot; + max);
</code></pre>
<hr>
<h2 id="array와-arraylist의-차이">Array와 ArrayList의 차이</h2>
<ul>
<li>배열은 크기가 고정되지만, ArrayList는 동적으로 크기가 변경됩니다.</li>
<li>배열은 기본 자료형을 저장할 수 있지만, ArrayList는 객체만 저장 가능합니다.</li>
</ul>
<hr>
<p>빠르고 간단하게 Array에 대해서 review 해봤습니다. 오늘도 error / issue 없는 하루 되시길 바랍니다. 감사합니다 :) </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Coding Test 공부 개요]]></title>
            <link>https://velog.io/@00kang_jh/Coding-Test-%EA%B3%B5%EB%B6%80-%EA%B0%9C%EC%9A%94</link>
            <guid>https://velog.io/@00kang_jh/Coding-Test-%EA%B3%B5%EB%B6%80-%EA%B0%9C%EC%9A%94</guid>
            <pubDate>Wed, 01 Jan 2025 06:01:44 GMT</pubDate>
            <description><![CDATA[<p>4년의 학부생활은 마쳤고 취업을 위해서 할 수 있는 것들 중 하나인 coding Test 준비를 해보려고 합니다. coding Test는 어쩌면 가장 개발자다운 면접이라고 생각합니다. 또 가장 흔하게도 보이는 방식이면서도 가장 어려운 방식이기도 합니다. 무엇이든 짧은 시간에 이루어내는 것이 어렵습니다. 하지만 이 어려운 걸 이번 기회에 해보려고 합니다. 뭐든 노력해서 안되는 것은 없다. </p>
<ol>
<li>자료구조 공부 및 문제 풀이</li>
<li>빅오 표기법 (big-O nation) 익히기</li>
<li>알고리즘 공부 및 문제 풀이</li>
<li>알고리즘 분석</li>
</ol>
<p>다음과 같은 순서로 진행하려고 합니다.</p>
<hr>
<h3 id="1-자료구조data-structure-공부-및-문제-풀이">1. 자료구조(Data structure) 공부 및 문제 풀이</h3>
<p>앞으로 공부할 자료구조는 <strong>Array, String, Stack, Queue, Priority Queue, Deque, Hash</strong> 정도 입니다. 자바로 coding test를 준비할 예정이기 때문에 *<em>이론 공부 -&gt; 자바 내장 클래스의 메소드 공부 -&gt; 백준 문제 풀이 *</em> 순서로 진행할 예정입니다. </p>
<h4 id="--이론-공부-간단-요약">- 이론 공부 간단 요약</h4>
<pre><code>Array: 배열의 구조와 특징, 탐색 및 정렬 알고리즘
String: 문자열의 처리 방법, 다양한 문자열 메소드
Stack: LIFO 구조, 다양한 활용 예시
Queue: FIFO 구조, 일반 큐와 원형 큐의 차이점
Priority Queue: 우선순위 큐의 개념과 활용
Deque: 양쪽 끝에서 삽입과 삭제가 가능한 자료구조
Hash: 해시 테이블의 원리, 충돌 해결 방식</code></pre><h4 id="--자바-내장-클래스--간단-요약">- 자바 내장 클래스  간단 요약</h4>
<pre><code>Array: Arrays 클래스의 메소드 (정렬, 검색 등)
String: String 클래스의 주요 메소드
Stack: Stack 클래스 (push, pop, peek 등)
Queue: Queue 인터페이스와 LinkedList 또는 ArrayDeque 구현
Priority Queue: PriorityQueue 클래스
Deque: Deque 인터페이스와 ArrayDeque 클래스
Hash: HashMap, HashSet 클래스의 메소드</code></pre><p>백준 문제 풀이의 경우에는 하루 평균 3~5문제 풀 예정입니다.</p>
<hr>
<h3 id="2-빅오-표기법-big-o-nation-익히기">2. 빅오 표기법 (big-O nation) 익히기</h3>
<p>알고리즘 공부하기 앞서서 빅오 표기법을 공부하려고 합니다. 해당 표기법은 알고리즘에서 가장 기초가 되는 부분으로 알고리즘의 시간 복잡도, 공간 복잡도를 나타낼 수 있는 수학적 기법입니다. 공부해야하는 이유는 효율성 평가, 최적화, 문제 해결, 면접 준비에 필요하고 필수적이기 때문입니다.</p>
<pre><code>효율성 평가: 알고리즘의 성능을 평가하고 다른 알고리즘과 비교할 수 있습니다.
최적화: 성능을 개선하기 위해 어떤 부분을 최적화해야 하는지 이해할 수 있습니다.
문제 해결: 특정 문제에 적합한 알고리즘을 선택하는 데 도움을 줍니다.
면접 준비: 많은 코딩 테스트 인터뷰에서 알고리즘의 복잡도를 물어보므로 필수적인 지식입니다.</code></pre><p>기본 개념 정리 후 실제 코드를 보면서 시간 복잡도, 공간 복잡도를 구해보면서 공부할 예정입니다.</p>
<hr>
<h3 id="3-알고리즘-공부-및-문제-풀이">3. 알고리즘 공부 및 문제 풀이</h3>
<p>앞으로 공부할 알고리즘에 종류에서 정<strong>렬(quick sort, merge sort), 탐색(binary search, DFS, BFS), 재귀, DP, Graph</strong>를 공부할 예정입니다. 해당 공부는 <strong>이론 공부 -&gt; 백준 문제(실버) -&gt; 백준 문제(골드)</strong> 순서로 진행할 예정입니다. 하루 평균 3~5문제정도 풀이할 예정입니다.</p>
<h4 id="--이론-공부-간단-요약-1">- 이론 공부 간단 요약</h4>
<ol>
        <li>
            <strong>정렬 알고리즘</strong>
            <ul>
                <li>퀵 정렬: 평균 O(n log n), 분할 정복 방식.</li>
                <li>병합 정렬: O(n log n), 안정적인 정렬.</li>
            </ul>
        </li>
        <li>
            <strong>탐색 알고리즘</strong>
            <ul>
                <li>이진 탐색: O(log n), 정렬된 배열에서 특정 원소 찾기.</li>
                <li>DFS (깊이 우선 탐색): 재귀적 또는 스택을 이용한 탐색.</li>
                <li>BFS (너비 우선 탐색): 큐를 이용하여 레벨별 탐색.</li>
            </ul>
        </li>
        <li>
            <strong>재귀</strong>
            <ul>
                <li>자기 자신을 호출하여 문제를 해결하는 기법.</li>
                <li>기본 구조와 종료 조건 이해.</li>
            </ul>
        </li>
        <li>
            <strong>동적 프로그래밍 (DP)</strong>
            <ul>
                <li>문제를 더 작은 하위 문제로 나누어 해결.</li>
                <li>메모이제이션 또는 타뷸레이션 기법 사용.</li>
            </ul>
        </li>
        <li>
            <strong>그래프</strong>
            <ul>
                <li>그래프 표현 방법 (인접 행렬, 인접 리스트).</li>
                <li>그래프 탐색 (DFS, BFS)과 최단 경로 알고리즘 이해.</li>
            </ul>
        </li>
    </ol>

<hr>
<h3 id="4-알고리즘-분석">4. 알고리즘 분석</h3>
<p>알고리즘 분석은 주어진 문제를 해결하기 위해 작성한 코드의 성능을 평가하는 과정입니다. 이를 통해 알고리즘의 효율성을 이해하고, 최적화할 수 있는 기회를 제공합니다. 앞으로의 알고리즘 분석은 다음과 같은 방법으로 진행할 예정입니다:</p>
<ol>
    <li>
        <strong>시간 복잡도 계산</strong>
        <ul>
            <li>코드의 실행 시간이 입력 크기에 따라 어떻게 변화하는지를 분석합니다.</li>
            <li>반복문이나 재귀 호출의 횟수를 세고, Big O 표기법으로 표현합니다.</li>
            <li>예를 들어, 중첩 반복문이 있는 경우 O(n<sup>2</sup>)로 표현할 수 있습니다.</li>
        </ul>
    </li>
    <li>
        <strong>공간 복잡도 계산</strong>
        <ul>
            <li>코드가 사용하는 메모리의 양을 분석합니다.</li>
            <li>변수, 배열, 객체 등에서 사용하는 메모리의 양을 고려합니다.</li>
            <li>재귀 호출로 인해 사용하는 스택 메모리를 포함하여 평가합니다.</li>
        </ul>
    </li>
    <li>
        <strong>다른 사람의 코드 분석</strong>
        <ul>
            <li>다른 사람의 알고리즘을 분석하여 다양한 접근 방식을 배웁니다.</li>
            <li>코드의 시간 복잡도와 공간 복잡도를 이해합니다.</li>
            <li>자신의 코드와 비교하여 개선할 수 있는 부분을 찾아봅니다.</li>
        </ul>
    </li>
</ol>

<p>이러한 단계들을 통해 알고리즘 분석의 기초를 다지고, 문제 해결 능력을 향상시키는 데 중점을 둘 것입니다. 알고리즘의 성능을 체계적으로 평가함으로써, 더 효율적인 코드 작성과 문제 해결 전략을 개발할 수 있도록 노력하겠습니다.</p>
<hr>
<p>happy new year :)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[코딩 테스트 필수 문법 (2)]]></title>
            <link>https://velog.io/@00kang_jh/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%95%84%EC%88%98-%EB%AC%B8%EB%B2%95-2</link>
            <guid>https://velog.io/@00kang_jh/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%95%84%EC%88%98-%EB%AC%B8%EB%B2%95-2</guid>
            <pubDate>Thu, 14 Nov 2024 01:54:05 GMT</pubDate>
            <description><![CDATA[<h3 id="컬렉션-프레임-워크collection-framework">컬렉션 프레임 워크(Collection Framework)</h3>
<hr>
<p>여러 개의 값을 저장하고 그 값을 쉽고, 효율적으로 처리해주는 표준화 클래스의 집합 입니다. 쉽게 말하자면 Data structure인 리스트, 큐, 스택, 해시맵 등을 직접 구현하지 않고 쉽게 사용할 수 있습니다. 대표적으로 <code>ArrayList</code> , <code>Stack</code>, <code>Queue</code>, <code>ArrayDeque</code>, <code>HashMap</code> 등이 있습니다.</p>
<p><strong>컬렉션 프레임워크의 주요 클래스</strong></p>
<ul>
<li><strong>ArrayList</strong>
ArrayList는 가변 크기를 가지며, 요소를 인덱스에 따라 저장하는 리스트입니다.<pre><code class="language-java">import java.util.ArrayList;
</code></pre>
</li>
</ul>
<p>public class ArrayListExample {
    public static void main(String[] args) {
        // Create
        ArrayList<String> list = new ArrayList&lt;&gt;();
        list.add(&quot;Apple&quot;);
        list.add(&quot;Banana&quot;);</p>
<pre><code>    // Read
    System.out.println(list.get(0)); // Apple

    // Update
    list.set(1, &quot;Cherry&quot;);
    System.out.println(list.get(1)); // Cherry

    // Delete
    list.remove(0);
    System.out.println(list); // [Cherry]
}</code></pre><p>}</p>
<pre><code>- **Stack**
Stack은 `LIFO(Last In First Out)` 구조로, 요소를 쌓아 올리는 형식입니다.

```java
import java.util.Stack;

public class StackExample {
    public static void main(String[] args) {
        // Create
        Stack&lt;String&gt; stack = new Stack&lt;&gt;();
        stack.push(&quot;A&quot;);
        stack.push(&quot;B&quot;);

        // Read
        System.out.println(stack.peek()); // B

        // Update (Stack에서는 update가 아닌 pop 후 push로 처리)
        stack.pop(); // 제거
        stack.push(&quot;C&quot;); // 새 요소 추가

        // Delete
        stack.pop(); // C 제거
        System.out.println(stack); // [A]
    }
}</code></pre><ul>
<li><strong>Queue</strong>
Queue는 <code>FIFO(First In First Out)</code> 구조로, 먼저 들어온 요소가 먼저 나갑니다.</li>
</ul>
<pre><code class="language-java">import java.util.LinkedList;
import java.util.Queue;

public class QueueExample {
    public static void main(String[] args) {
        // Create
        Queue&lt;String&gt; queue = new LinkedList&lt;&gt;();
        queue.add(&quot;1&quot;);
        queue.add(&quot;2&quot;);

        // Read
        System.out.println(queue.peek()); // 1

        // Update (Queue는 직접적으로 업데이트 불가, 제거 후 추가)
        queue.poll(); // 1 제거
        queue.add(&quot;3&quot;); // 새 요소 추가

        // Delete
        queue.poll(); // 2 제거
        System.out.println(queue); // [3]
    }
}</code></pre>
<ul>
<li><strong>ArrayDeque</strong>
ArrayDeque는 덱(Double Ended Queue)으로, 양쪽 끝에서 요소를 추가하거나 제거할 수 있습니다.</li>
</ul>
<pre><code class="language-java">import java.util.ArrayDeque;

public class ArrayDequeExample {
    public static void main(String[] args) {
        // Create
        ArrayDeque&lt;String&gt; deque = new ArrayDeque&lt;&gt;();
        deque.add(&quot;X&quot;);
        deque.add(&quot;Y&quot;);

        // Read
        System.out.println(deque.peekFirst()); // X

        // Update (Update가 아닌 제거 후 추가)
        deque.pollFirst(); // X 제거
        deque.addFirst(&quot;Z&quot;); // 새 요소 추가

        // Delete
        deque.pollLast(); // Y 제거
        System.out.println(deque); // [Z]
    }
}</code></pre>
<ul>
<li><strong>HashMap</strong>
HashMap은 키-값 쌍으로 데이터를 저장하며, 키를 통해 값을 빠르게 조회할 수 있습니다.</li>
</ul>
<pre><code class="language-java">import java.util.HashMap;

public class HashMapExample {
    public static void main(String[] args) {
        // Create
        HashMap&lt;String, Integer&gt; map = new HashMap&lt;&gt;();
        map.put(&quot;Apple&quot;, 1);
        map.put(&quot;Banana&quot;, 2);

        // Read
        System.out.println(map.get(&quot;Apple&quot;)); // 1

        // Update
        map.put(&quot;Apple&quot;, 3); // 값 업데이트
        System.out.println(map.get(&quot;Apple&quot;)); // 3

        // Delete
        map.remove(&quot;Banana&quot;);
        System.out.println(map); // {Apple=3}
    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[코딩 테스트 필수 문법 (1)]]></title>
            <link>https://velog.io/@00kang_jh/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%95%84%EC%88%98-%EB%AC%B8%EB%B2%951</link>
            <guid>https://velog.io/@00kang_jh/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%95%84%EC%88%98-%EB%AC%B8%EB%B2%951</guid>
            <pubDate>Thu, 14 Nov 2024 01:31:24 GMT</pubDate>
            <description><![CDATA[<h3 id="프리미티브-타입과-레퍼런스-타입">프리미티브 타입과 레퍼런스 타입</h3>
<hr>
<p><strong>프리미티브 타입(Primitive type)</strong>
원시 타입 은 정수, 실수, 문자, 논리 리터럴 등의 실제 데이터 값을 저장하는 타입입니다.</p>
<p><code>int</code>, <code>long</code>, <code>double</code>, <code>float</code>, <code>boolean</code>, <code>byte</code>, <code>short</code>, <code>char</code></p>
<p><strong>레퍼런스 타입(Reference Type)</strong>
참조 타입 은 객체(Object)를 참조(주소를 저장) 하는 타입으로 메모리 번지 값을 통해 객체를 참조하는 타입이다.</p>
<p><code>Integer</code>, <code>Long</code>, <code>Double</code>, <code>Float</code>, <code>Boolean</code>, <code>Byte</code>, <code>Short</code>, <code>Char</code></p>
<blockquote>
<p>연산 속도는 Primitive type가 Reference Type보다 빠릅니다.</p>
</blockquote>
<ul>
<li><strong>비교 연산</strong><pre><code class="language-java">System.out.println(a==b); // 같은 값인지 비교, false
System.out.println(a!=b); // 같지 않은 값인지 비교, false
System.out.println(a&gt;b);  // 왼쪽 같이 더 큰지 비교, false
System.out.println(a&lt;b);  // 왼쪽 값이 더 작은지 비교, false
System.out.println(a&gt;=b); // 왼쪽 값이 더 크거나 같은지 비교, false
System.out.println(a&lt;=b); // 왼쪽 값이 더 작거나 같은지 비교, false</code></pre>
</li>
<li><strong>비트 연산</strong><pre><code class="language-java">int a = 13;
int b = 4;
System.out.println(a &amp; b); // AND, 4
System.out.println(a | b); // OR, 13
System.out.println(a ^ b); // XOR, 9
System.out.println(~ a); // NOT, -14
System.out.println(a &lt;&lt; 2); // 왼쪽 시프트 (a에 2^2를 곱한 것과 동일) / 52
System.out.println(a &gt;&gt; 1); // 오른쪽 시프트 (a에 2^1로 나눈 것과 동일) / 6</code></pre>
</li>
</ul>
<p>부동소수형 데이터는 표현 과정에서 오차가 발생할 수 있다. 이것을 <code>엡실론(epsilon)</code>이라고 하며 항상 생각해야합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[OpenCV와 C++ 설치하기: Apple Silicon M2에서 VS Code 사용하기 🖥️]]></title>
            <link>https://velog.io/@00kang_jh/Computer-Vision-0</link>
            <guid>https://velog.io/@00kang_jh/Computer-Vision-0</guid>
            <pubDate>Mon, 14 Oct 2024 02:23:03 GMT</pubDate>
            <description><![CDATA[<p>OpenCV는 컴퓨터 비전 분야에서 널리 사용되는 라이브러리로, 이미지와 비디오 처리에 강력한 기능을 제공합니다. C++로 OpenCV를 사용하면 성능을 극대화할 수 있습니다. 이번 포스팅에서는 Apple Silicon M2에서 VS Code를 사용하여 OpenCV와 C++ 환경을 설정하는 방법을 자세히 알아보겠습니다.</p>
<h3 id="1개발환경-안내">1.개발환경 안내</h3>
<p>본인은 MacBook Air, Apple M2, macOS Sonoma 14.6.2 입니다.</p>
<h3 id="2-opencv와-c-소개">2. OpenCV와 C++ 소개</h3>
<p>OpenCV는 다양한 이미지 처리 기능을 제공하는 오픈 소스 라이브러리입니다. C++는 OpenCV와 함께 사용할 때 성능이 뛰어나기 때문에 많은 개발자들이 선호합니다. OpenCV를 통해 이미지 필터링, 객체 인식, 얼굴 인식 등 다양한 작업을 수행할 수 있습니다.</p>
<h3 id="3-apple-silicon-m2에서의-환경-설정">3. Apple Silicon M2에서의 환경 설정</h3>
<p>Apple Silicon M2는 ARM 아키텍처를 기반으로 하여, 기존의 Intel 기반 Mac과는 다른 환경을 제공합니다. 따라서, M2에서 OpenCV와 C++를 설치하기 위해서는 몇 가지 추가적인 설정이 필요합니다.</p>
<h3 id="4-vs-code-설치-및-설정">4. VS Code 설치 및 설정</h3>
<p>먼저, Visual Studio Code(VS Code)를 설치해야 합니다. VS Code는 다양한 언어를 지원하는 강력한 코드 편집기입니다. Apple Silicon에 맞는 버전을 다운로드하려면 VS Code 공식 웹사이트로 가서 Apple Silicon 버전을 선택하여 설치합니다.</p>
<p>설치가 완료되면, VS Code를 실행하고 C++ 확장 프로그램을 설치합니다. 이를 통해 C++ 개발 환경을 더욱 편리하게 사용할 수 있습니다.</p>
<h3 id="5-homebrew-설치-및-opencv-설치">5. Homebrew 설치 및 OpenCV 설치</h3>
<p>Homebrew는 macOS에서 패키지를 관리하는 도구입니다. Homebrew를 설치하려면 터미널을 열고 아래의 명령어를 입력합니다:</p>
<blockquote>
<p>bash
/bin/bash -c &quot;$(curl -fsSL <a href="https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)&quot;">https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)&quot;</a></p>
</blockquote>
<p>설치가 완료되면, OpenCV를 설치할 수 있습니다. 터미널에 다음 명령어를 입력하여 OpenCV를 설치합니다:</p>
<blockquote>
<p>bash
brew install opencv</p>
</blockquote>
<p>설치가 완료되면, OpenCV의 경로를 확인하고 환경 변수를 설정해야 합니다. 아래의 명령어를 입력하여 OpenCV의 설치 경로를 확인합니다:</p>
<blockquote>
<p>bash
brew info opencv</p>
</blockquote>
<p>이제 VS Code에서 OpenCV를 사용할 수 있도록 설정합니다. VS Code의 설정 파일에 OpenCV의 경로를 추가합니다.</p>
<h3 id="6-opencv와-c-프로젝트-설정">6. OpenCV와 C++ 프로젝트 설정</h3>
<p>이제 OpenCV와 C++ 프로젝트를 설정할 차례입니다. VS Code에서 새로운 C++ 프로젝트를 생성하고, CMakeLists.txt 파일을 생성하여 OpenCV 라이브러리를 포함시킵니다. 아래는 기본적인 CMakeLists.txt의 예시입니다:</p>
<blockquote>
<p>cmake
cmake_minimum_required(VERSION 3.10)
project(<span style="color:blue">프로젝트명</span>) <br> 
find_package(OpenCV REQUIRED)
add_executable(<span style="color:blue">프로젝트명</span>   <span style="color:green">파일명</span>)
target_link_libraries (<span style="color:blue">프로젝트명</span> ${OpenCV_LIBS}) </p>
</blockquote>
<p>이 코드는 지정한 이미지를 불러와서 화면에 표시하는 간단한 예제입니다. 이미지를 불러올 수 없는 경우 오류 메시지를 출력합니다.</p>
<h3 id="7-프로젝트-실행">7. 프로젝트 실행</h3>
<p>CMakeLists.txt를 완성한 후, 우리가 원하는 프로젝트를 실행할 차례입니다. VScode 터미널에서 실행하게 됩니다. 다음 명령어들을 입력하면 됩니다.</p>
<blockquote>
<p>cd build
make
./<span style="color:blue">프로젝트명</span></p>
</blockquote>
<p>다음과 같이 입력하시게 되면 CMakeLists.txt에서 설정한 파일이 실행됨을 알 수 있습니다. 실행한 프로젝트를 종료하시려면 <code>control + z</code> 누르시면 됩니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 멀쩡한 사각형 Java]]></title>
            <link>https://velog.io/@00kang_jh/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%A9%80%EC%A9%A1%ED%95%9C-%EC%82%AC%EA%B0%81%ED%98%95-Java</link>
            <guid>https://velog.io/@00kang_jh/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%A9%80%EC%A9%A1%ED%95%9C-%EC%82%AC%EA%B0%81%ED%98%95-Java</guid>
            <pubDate>Tue, 08 Oct 2024 07:05:47 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이번 문제는 프로그래머스 Level 2 [멀쩡한 사각형] (<a href="https://school.programmers.co.kr/learn/courses/30/lessons/62048">https://school.programmers.co.kr/learn/courses/30/lessons/62048</a>) 입니다.</p>
</blockquote>
<hr>
<h3 id="문제-설명">문제 설명</h3>
<p>가로 길이가 <strong>W</strong>cm, 세로 길이가 <strong>H</strong>cm인 직사각형 종이가 있습니다. 종이에는 가로, 세로 방향과 평행하게 격자 형태로 선이 그어져 있으며, 모든 격자칸은 1cm x 1cm 크기입니다. 이 종이를 격자 선을 따라 1cm × 1cm의 정사각형으로 잘라 사용할 예정이었는데, 누군가가 <strong>이 종이를 대각선 꼭지점 2개를 잇는 방향으로 잘라 놓았습니다.</strong> 그러므로 현재 직사각형 종이는 크기가 같은 직각삼각형 2개로 나누어진 상태입니다. 새로운 종이를 구할 수 없는 상태이기 때문에, 이 종이에서 원래 종이의 가로, 세로 방향과 평행하게 1cm × 1cm로 잘라 사용할 수 있는 만큼만 사용하기로 하였습니다.</p>
<p>가로의 길이 W와 세로의 길이 H가 주어질 때, 사용할 수 있는 정사각형의 개수를 구하는 solution 함수를 완성해 주세요.</p>
<blockquote>
<p><strong>한줄로 요약하면!!</strong>
가로/세로 값이 해당 사이즈의 직사각형을 반절로 접었을 때, 그 대각선을 지나는 칸을 제외한 나머지 칸을 구하면 됩니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/00kang_jh/post/a9d1689b-8cdb-4c61-b7e0-02bdc85818bf/image.png" alt=""></p>
<hr>
<h3 id="제한사항">제한사항</h3>
<p>W, H : 1억 이하의 자연수</p>
<hr>
<h3 id="입출력-예시">입출력 예시</h3>
<p>w = 8 , h = 12 일때, 전체 칸은 96칸 입니다.
그 직사각형의 대각선을 그립니다. 해당 대각선이 지나는 칸이 총 16칸이기 때문에 그 만큼을 제외한 80칸이 return 됩니다. </p>
<hr>
<h3 id="코드-작성-전-생각-정리">코드 작성 전, 생각 정리</h3>
<ul>
<li>파라미터가 int 타입인데, return 타입이 long이라 계산할 때 타입 변환하기</li>
<li>다른 값들 통해서 규칙성을 찾기<ul>
<li>위의 사진을 보면, 총 4번의 동일한 패턴을 보인다. </li>
</ul>
</li>
</ul>
<blockquote>
<p><strong>여기서 4라는 숫자가 어떻게 나올 수 있을까?</strong>
예제에서 8과 12일 때, 4라는 숫자는 여러가지로 생각해볼 수 있지만 최대공약수로 추측할 수 있다. 그 외의 경우에도 고려해보면 공약수가 없는 경우를 제외하면 모든 경우에 최대공약수로 결과값을 구하면 원하는 값과 동일하다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/00kang_jh/post/eb912aaf-f1a1-4bc7-823a-27dfd5215f07/image.png" alt=""></p>
<ol>
<li><p>(2,3)일 때, 4개의 직사각형이 영향을 받음</p>
</li>
<li><p>(5,2)일 때, 6개의 직사각형이 영향을 받음</p>
<p>일반식으로 만들어보면!!
영향을 받는 직사각형의 수 = (너비) + (높이) - 1 이 되고,
결론적으로 전체 계산식을 작성하면
<strong>(너비) x (높이) - ((너비) + (높이) - 1) x (최대공약수)</strong>이 됩니다.</p>
</li>
</ol>
<hr>
<h3 id="코드자바">코드(자바)</h3>
<pre><code class="language-java">class Solution {
    public long solution(int w, int h) {
        long answer = 1;
        long big = gcd(w,h);
        long weight = (long)w;
        long height = (long)h;

        answer = weight*height - ((weight/big)+(height/big)-1)*big;

        return answer;
    }

    public static long gcd(int a, int b) {
        while (b != 0) {
            int temp = b;
            b = a % b;
            a = temp;
        }
        return (long)a;
    }
}
</code></pre>
<hr>
<h3 id="개인-소감">개인 소감</h3>
<p>규칙성을 찾는데 시간이 걸렸다. 매번 코딩 테스트 문제만 풀이하면서는 &#39;어떤 라이브러리의 어떤 메소드를 사용하면 좋을까?&#39; 이런 고민들을 많이 했었다. 너무나도 많은 라이브러리에 메소드를 기억하기 어려워서 찾아서 공부하고, 찾아서 코드에 작성하는 형식이였다. 이번 문제는 다른 문제들과 다르게 오랜만에 라이브러리와 메소드를 고민하는 것보단 고등학교 시절로 돌아가 수학 문제를 풀던 때로 돌아간 기분이였다. 뭐든 안하면 잊어버리고 머리가 굳기 마련이다. 굳지 않고 말랑말랑한 상태 유지를 위해서 열심히 노력하자!!</p>
<hr>
<p><img src="https://velog.velcdn.com/images/00kang_jh/post/3192bb25-ca03-4bb8-8855-390e3730e93e/image.png"  width="40%" height="40%"><p></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 기능개발 Java]]></title>
            <link>https://velog.io/@00kang_jh/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B8%B0%EB%8A%A5%EA%B0%9C%EB%B0%9C-Java</link>
            <guid>https://velog.io/@00kang_jh/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B8%B0%EB%8A%A5%EA%B0%9C%EB%B0%9C-Java</guid>
            <pubDate>Thu, 03 Oct 2024 11:26:23 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이번 문제는 프로그래머스 Level 2 <a href="https://school.programmers.co.kr/learn/courses/30/lessons/42586">기능개발</a> 입니다.</p>
</blockquote>
<h3 id="문제-설명">문제 설명</h3>
<p>프로그래머스 팀에서는 기능 개선 작업을 수행 중입니다. 각 기능은 진도가 100%일 때 서비스에 반영할 수 있습니다.</p>
<p>또, 각 기능의 개발속도는 모두 다르기 때문에 뒤에 있는 기능이 앞에 있는 기능보다 먼저 개발될 수 있고,
이때 뒤에 있는 기능은 앞에 있는 기능이 배포될 때 함께 배포됩니다.</p>
<p>먼저 배포되어야 하는 순서대로 작업의 진도가 적힌 정수 배열 progresses와 각 작업의 개발 속도가 적힌 정수 배열 speeds가 주어질 때 각 배포마다 몇 개의 기능이 배포되는지를 return 하도록 solution 함수를 완성하세요.</p>
<hr>
<h3 id="제한-사항">제한 사항</h3>
<ol>
<li>작업의 개수(progresses, speeds배열의 길이)는 100개 이하입니다.</li>
<li>작업 진도는 100 미만의 자연수입니다.</li>
<li>작업 속도는 100 이하의 자연수입니다.</li>
<li>배포는 하루에 한 번만 할 수 있으며, 하루의 끝에 이루어진다고 가정합니다. 예를 들어 진도율이 95%인 작업의 개발 속도가 하루에 4%라면 배포는 2일 뒤에 이루어집니다.</li>
</ol>
<hr>
<h3 id="입출력-예시">입출력 예시</h3>
<p><img src="https://velog.velcdn.com/images/00kang_jh/post/a664ad5e-51fd-4b09-b579-062dc0e2c51e/image.png" alt=""></p>
<p>progresses: 현재 진행률
speeds: 하루당 개발 속도
return: 배포 당시 구현 완료된 기능의 수</p>
<hr>
<h3 id="문제-요약">문제 요약</h3>
<p>progresses의 순서는 우선순위를 의미하기 때문에 바꾸지 않고, 앞에 있는 기능 개발이 완료되어야 뒤에 있는 기능들도 같이 배포할 수 있습니다. 각기 다른 진행 속도를 갖기 때문에 뒤에 있는 것이 모두 진행이 완료되어도 앞에 있는 기능이 완료되지 않으면 배포가 불가능합니다.</p>
<hr>
<h3 id="문제-풀이-전-생각-정리">문제 풀이 전 생각 정리</h3>
<ul>
<li>큐를 이용해서 풀면 될꺼 같습니다. 큐의 성질인 (FIFO)를 이용합니다다.</li>
</ul>
<ol>
<li>progresses의 값을 큐에 넣고 speeds를 기반으로 증가를 시켜줍니다.</li>
<li>100이 넘는 숫자들은 remove 하고, answer의 값을 증가시킵니다.</li>
<li>이 과정을 반복 후, answer를 return 합니다.</li>
</ol>
<hr>
<h3 id="코드">코드</h3>
<pre><code class="language-java">import java.util.LinkedList;
import java.util.Queue;
import java.util.ArrayList;

class Solution {
    public ArrayList solution(int[] progresses, int[] speeds) {
        ArrayList&lt;Integer&gt; answer = new ArrayList&lt;Integer&gt;(); //추가하는데 편의를 위해서 ArrayList 사용
        Queue&lt;Integer&gt; works = new LinkedList&lt;&gt;();

        // Queue에 progresses값으로 100%까지 걸리는 일수 저장하기
        for(int i =0; i&lt; progresses.length; i++){ 
            float cal = (100-progresses[i])/speeds[i];
            if((100.0-progresses[i])%speeds[i] != 0) cal++;
            works.offer((int)cal);
        }

        // 배포를 위한 검사 시작
        while(!works.isEmpty()){
            int work = works.poll(); // 제일 앞에 있는 것을 하나 꺼냄.
            int count = 1;

            //work보다 작은 것들의 수를 셈. 배포할 때, 몇개의 기능을 배포할 수 있는지 파악하기 위해서
            while(!works.isEmpty()){
                if(work &lt; works.peek()) break; // work보다 크면 그 기능 뒤부터는 배포 불가능해서 종료함.
                works.remove();
                count++;
            }
            answer.add(count); // answer에 추가해줌.
        }

        works.clear();
        return answer;
    }
}</code></pre>
<hr>
<h3 id="개인-소감">개인 소감</h3>
<p>난이도는 level2 치고는 어렵지 않았던 것 같다. 풀이를 생각하는데 어려움이 있었던 건 아니고 아무래도 Queue, ArrayList, LinkedList와 같이 자바 내장 라이브러리를 사용을 해야해서 level2가 아닌가 싶었다. 그리고 큐를 사용하지 않고 문제를 풀이할 수 있을거 같다는 생각이 들긴 하였다. 나중에 시간이 되면 배열로도 한번 풀어봐야겠다.</p>
<hr>
<p><img src="https://velog.velcdn.com/images/00kang_jh/post/d3231d96-f723-478f-b859-f18a02e7890f/image.png"  width="20%" height="20%"><p></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 문자열 압축 Java]]></title>
            <link>https://velog.io/@00kang_jh/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%95%95%EC%B6%95-Java</link>
            <guid>https://velog.io/@00kang_jh/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%95%95%EC%B6%95-Java</guid>
            <pubDate>Thu, 26 Sep 2024 17:46:50 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이번 문제는 2020 KAKAO BLIND RECRUITMENT에서 출제되었고 level 2 <a href="https://school.programmers.co.kr/learn/courses/30/lessons/60057">문자열 압축</a> 입니다.</p>
</blockquote>
<h4 id="문제-설명">문제 설명</h4>
<p>데이터 처리 전문가가 되고 싶은 &quot;어피치&quot;는 문자열을 압축하는 방법에 대해 공부를 하고 있습니다. 최근에 대량의 데이터 처리를 위한 간단한 비손실 압축 방법에 대해 공부를 하고 있는데, <strong>문자열에서 같은 값이 연속해서 나타나는 것을 그 문자의 개수와 반복되는 값으로 표현하여 더 짧은 문자열</strong>로 줄여서 표현하는 알고리즘을 공부하고 있습니다.
간단한 예로 &quot;aabbaccc&quot;의 경우 &quot;2a2ba3c&quot;(문자가 반복되지 않아 한번만 나타난 경우 1은 생략함)와 같이 표현할 수 있는데, 이러한 방식은 반복되는 문자가 적은 경우 압축률이 낮다는 단점이 있습니다. 예를 들면, &quot;abcabcdede&quot;와 같은 문자열은 전혀 압축되지 않습니다. &quot;어피치&quot;는 이러한 단점을 해결하기 위해 문자열을 1개 이상의 단위로 잘라서 압축하여 더 짧은 문자열로 표현할 수 있는지 방법을 찾아보려고 합니다.</p>
<p>예를 들어, &quot;ababcdcdababcdcd&quot;의 경우 문자를 1개 단위로 자르면 전혀 압축되지 않지만, 2개 단위로 잘라서 압축한다면 &quot;2ab2cd2ab2cd&quot;로 표현할 수 있습니다. 다른 방법으로 8개 단위로 잘라서 압축한다면 &quot;2ababcdcd&quot;로 표현할 수 있으며, 이때가 가장 짧게 압축하여 표현할 수 있는 방법입니다.</p>
<p>다른 예로, &quot;abcabcdede&quot;와 같은 경우, 문자를 2개 단위로 잘라서 압축하면 &quot;abcabc2de&quot;가 되지만, 3개 단위로 자른다면 &quot;2abcdede&quot;가 되어 3개 단위가 가장 짧은 압축 방법이 됩니다. 이때 3개 단위로 자르고 마지막에 남는 문자열은 그대로 붙여주면 됩니다.</p>
<p>압축할 문자열 s가 매개변수로 주어질 때, 위에 설명한 방법으로 1개 이상 단위로 문자열을 잘라 압축하여 표현한 문자열 중 가장 짧은 것의 길이를 return 하도록 solution 함수를 완성해주세요.</p>
<hr>
<h4 id="제한-사항">제한 사항</h4>
<p>s의 길이가 1000이하, 모두 소문자입니다.</p>
<hr>
<h4 id="입출력-예시">입출력 예시</h4>
<body>
<p align="center"><img src="https://velog.velcdn.com/images/00kang_jh/post/05e1acd7-17e6-4202-b6b0-3a2b5461ed6c/image.png"  width="50%" height="50%"><p>
</body>

<hr>
<h4 id="문제-요약문제가-너무-길어">문제 요약(문제가 너무 길어,,,)</h4>
<p>Main point 반복되는 문자를 &quot;반복횟수 + 반복되는 문자&quot;로 대체하는데 최대한 짧게 만드는 것이다.</p>
<hr>
<h4 id="문제-풀이-전-생각-정리">문제 풀이 전 생각 정리</h4>
<ol>
<li>최소 위해서 최대로 초기화한다. answer를 문자열의 길이로 정하고 시작한다.</li>
<li>글자를 1 ~ s.length()/2로 순차적으로 잘라서 검사를 진행한다.</li>
</ol>
<p>-&gt; substring 사용하면 될 듯
3. 잘라서 검색하다가 반복이 되지 않으면 &quot;반복횟수 + 반복되는 문자&quot;로 저장한다. 
-&gt; 최소로 만드는 것이 목표이기 때문에 s를 다시 재사용 가능성 있음
4. 길이가 1인 경우는 그냥 return 1 처리</p>
<p>❗️ 굳이 문자열로 저장해서 문제를 풀어야할까?❗️
아무래도 문자열이 무거우니깐 단순 검사 통해서 하면 어떨까?</p>
<hr>
<h4 id="코드">코드</h4>
<pre><code class="language-java">import java.util.ArrayList;

class Solution {

    public int press(String str, int length) {
        StringBuilder sb = new StringBuilder(); // 줄인 문자열을 저장.

        String last = &quot;&quot;;
        int count = 0;
        for (int start = 0; start &lt; str.length(); start += length) {
            int end = start + length;

            if (end &gt; str.length()) end = str.length(); //길이가 오버돼서 에러나는 경우

            String token = str.substring(start, end); //압축할 단위

            if (token.equals(last)) count++;
            else {
                if (count &gt; 1) sb.append(count);
                sb.append(last);
                last = token;
                count = 1;
            }
        }
        if (count &gt; 1) sb.append(count); //1은 반복이 아니기 때문에 1보다 큰 값일 경우
        sb.append(last);

        return sb.length();
    }

    public int solution(String s) {
        int answer = s.length();
        if (s.length() == 1) return 1;

        for (int length = 1; length &lt;= s.length()/2; length++) {
            int pressed = press(s, length); // 압축했을 때 문자열의 길이
            if (pressed &lt; answer) { // 최소값 찾기
                answer = pressed;
            }
        }

        return answer;
    }
}</code></pre>
<hr>
<h4 id="개인-소감">개인 소감</h4>
<p>코딩 테스트 준비한지 얼마 되지 않아서 생각하는 것도 구현하는 것도 미숙한 부분들이 많다는 느낌을 많이 받았다. 그리고 문제 풀고 나서 개인적으로 문자열을 새롭게 만들어서 결과를 구하는 방법이 메모리 측면에서 비효율적이라는 생각이 들었다. 추후 문자열로 저장하지 않고 단순 검사를 통해서 결과값을 구해볼 예정이다.</p>
<p>to be continue,,,,</p>
<hr>
<p><img src="https://velog.velcdn.com/images/00kang_jh/post/abd765c7-2cc7-4960-bab7-632ac2236f8b/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[일단 출바알~]]></title>
            <link>https://velog.io/@00kang_jh/%EC%9D%BC%EB%8B%A8-%EC%B6%9C%EB%B0%94%EC%95%8C</link>
            <guid>https://velog.io/@00kang_jh/%EC%9D%BC%EB%8B%A8-%EC%B6%9C%EB%B0%94%EC%95%8C</guid>
            <pubDate>Tue, 10 Sep 2024 06:47:51 GMT</pubDate>
            <description><![CDATA[<p>예전부터 개발자 블로그나 기술 블로그 작성해보고 싶은 마음이 있었다.</p>
<p><strong>왜냐?!</strong>
개발하면서 수없이 많은 구글링 하면서 &#39;역시 기록만큼 남아서 좋은게 없다&#39; 는 생각도 했고 알지 못하는 에러에 고통 받고 있을 때 이런 블로그이 나에게 구원의 손길을 내밀었다...(모두들 감사해요🫶🏻)</p>
<p>그래서 나도 시작해본다. </p>
<p>사실 어떻게 하는진 잘 모르겠지만 일단 시작하는 거죠ㅎㅎㅎ
<img src="https://velog.velcdn.com/images/00kang_jh/post/fdefde8d-4326-4734-a276-1821847f023e/image.png" width="30%" height="30%"></p>
]]></description>
        </item>
    </channel>
</rss>