<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>oh_yunseong.log</title>
        <link>https://velog.io/</link>
        <description>To Be Outstanding, To Foster Understanding🚀</description>
        <lastBuildDate>Sun, 26 Oct 2025 13:50:52 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>oh_yunseong.log</title>
            <url>https://images.velog.io/images/oh_yunseong/profile/dbc7fc85-506a-4048-a017-1a3e825f469e/깃헙사진.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. oh_yunseong.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/oh_yunseong" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[센터교육]노션으로하는 업무관리]]></title>
            <link>https://velog.io/@oh_yunseong/%EC%84%BC%ED%84%B0%EA%B5%90%EC%9C%A1%EB%85%B8%EC%85%98%EC%9C%BC%EB%A1%9C%ED%95%98%EB%8A%94-%EC%97%85%EB%AC%B4%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@oh_yunseong/%EC%84%BC%ED%84%B0%EA%B5%90%EC%9C%A1%EB%85%B8%EC%85%98%EC%9C%BC%EB%A1%9C%ED%95%98%EB%8A%94-%EC%97%85%EB%AC%B4%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Sun, 26 Oct 2025 13:50:52 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>회사 내부에서 프로젝트 및 개발 관리 툴을 도입하고자 했다.
이전부터 노션으로 고객사와 협업 및 파트관리를 해왔었는데, 이를 보고 센터교육을 지시 해서 교육을 진행하게 됐다.</p>
</blockquote>
<h2 id="1️⃣-내가-노션을-사용하게-된-이유">1️⃣ 내가 노션을 사용하게 된 이유</h2>
<p>내가 노션을 도입하게 된 이유는 단순히 ‘문서화’ 때문이 아니었다.
프로젝트 운영 과정에서 반복되는 비효율과 혼선을 근본적으로 해결하고 싶었다.</p>
<p>매번 느꼈던 문제는 세 가지였다.</p>
<h3 id="💥-pain-point-1-고객사-커뮤니케이션-누락으로-인한-설계-혼선">💥 Pain Point 1. 고객사 커뮤니케이션 누락으로 인한 설계 혼선</h3>
<ul>
<li>비즈니스 로직 변경 사항이 문서로 정리되지 않음</li>
<li>구두 전달이 많아 개발 과정에서 맥락이 단절</li>
<li>결과적으로 재작업과 일정 지연 발생</li>
</ul>
<blockquote>
<p>✅ Solution: 회의록 기반의 협업 및 히스토리 관리
노션 데이터베이스를 중심으로 회의 내용을 실시간 기록하고
논의된 결정과 변경사항을 히스토리로 남겨 <strong>“설계의 맥락”</strong>을 보존했다.
모든 팀원이 같은 문서를 보며 일하니, 더 이상 “누가 최신 버전이냐”는 말이 사라졌다.</p>
</blockquote>
<h3 id="💥-pain-point-2-개발·배포-표준-컨벤션-관리의-어려움">💥 Pain Point 2. 개발·배포 표준 컨벤션 관리의 어려움</h3>
<ul>
<li>개발자마다 코드 스타일, 네이밍, 배포 방식이 달랐음</li>
<li>표준이 없어 산출물의 일관성이 떨어지고 유지보수 부담이 증가</li>
</ul>
<blockquote>
<p>✅ Solution: 개발 컨벤션 및 산출물 아카이빙
노션에 표준화된 개발 문서 템플릿을 만들어
명명규칙, 코드 스타일, 배포 방식까지 체계적으로 관리했다.
이후 산출물을 아카이빙하여 재사용성을 높이고
새 프로젝트에서도 바로 참고할 수 있도록 했다.</p>
</blockquote>
<h3 id="💥-pain-point-3-프로젝트-진행-현황의-가시성-부족">💥 Pain Point 3. 프로젝트 진행 현황의 가시성 부족</h3>
<ul>
<li>개발 일정, QA 이슈, 담당자별 진행률이 한눈에 안 보임</li>
<li>실시간 파악이 어렵고, 일정 관리에 차질 발생</li>
</ul>
<blockquote>
<p>✅ Solution: QA·진행 현황의 시각화 및 자동 관리
노션 데이터베이스를 기반으로 보드, 차트, 타임라인 뷰를 구성했다.
QA 요청부터 개발 진행 단계까지 한 화면에서 관리할 수 있었고,
태그와 상태 자동화로 “어디까지 진행됐는가?”를 즉시 확인할 수 있었다.</p>
</blockquote>
<h2 id="2️⃣-노션-접목--직급별-as-is--to-be-워크플로우-변화">2️⃣ 노션 접목 — 직급별 As-Is / To-Be 워크플로우 변화</h2>
<p>노션을 단순한 툴이 아닌 팀의 운영 체계로 바라봤을 때,
각 직급별로 일하는 방식이 완전히 달라졌다.</p>
<h3 id="👨💼-관리자파트장-이상급">👨‍💼 관리자(파트장 이상급)</h3>
<table>
<thead>
<tr>
<th>As-Is</th>
<th>To-Be</th>
</tr>
</thead>
<tbody><tr>
<td>업무 지시 및 보고가 모두 쪽지·면대면 중심</td>
<td><strong>팀 워크스페이스 기반 관리</strong><br>각 팀원이 자신의 업무, 일정, 진척도를 노션에 등록</td>
</tr>
<tr>
<td>설계 확인은 직접 문서 출력 후 검토</td>
<td><strong>공유 페이지에서 실시간 확인 및 피드백</strong></td>
</tr>
<tr>
<td>보고용 자료를 인별로 취합해야 함</td>
<td><strong>노션 DB를 그대로 취합하여 상위 보고 자동화</strong></td>
</tr>
</tbody></table>
<blockquote>
<p>📈 관리자 입장에서는 커뮤니케이션 비용이 줄고,
전체 프로젝트의 진행 상황을 한눈에 파악할 수 있게 됐다.</p>
</blockquote>
<h3 id="👨💻-개발자연구원선임급">👨‍💻 개발자(연구원~선임급)</h3>
<table>
<thead>
<tr>
<th>As-Is</th>
<th>To-Be</th>
</tr>
</thead>
<tbody><tr>
<td>상위직급에 수시로 보고 필요</td>
<td><strong>노션 템플릿 기반 업무 등록</strong>으로 실시간 공유</td>
</tr>
<tr>
<td>여러 업무가 겹치면 누락·중복 발생</td>
<td><strong>상태·기한 관리 속성</strong>으로 누락 방지</td>
</tr>
<tr>
<td>문서 양식이 제각각</td>
<td><strong>표준 템플릿 도입</strong>으로 문서 일관성 확보</td>
</tr>
</tbody></table>
<blockquote>
<p>⚙️ 개발자는 “보고”가 아닌 “기록”으로 자신의 업무를 남기며,
모든 업무가 자동으로 추적되고 관리된다.</p>
</blockquote>
<h3 id="🧑💼-고객사-pm--현업">🧑‍💼 고객사 PM / 현업</h3>
<table>
<thead>
<tr>
<th>As-Is</th>
<th>To-Be</th>
</tr>
</thead>
<tbody><tr>
<td>커뮤니케이션 누락, 설계 문서 버전 불일치</td>
<td><strong>공유 워크스페이스로 실시간 확인 및 공동 편집</strong></td>
</tr>
<tr>
<td>개발 일정·진척률 파악 어려움</td>
<td><strong>데이터베이스 기반 프로젝트 현황 대시보드 제공</strong></td>
</tr>
<tr>
<td>문의나 피드백이 비공식 채널에서 처리됨</td>
<td><strong>고객 전용 Q&amp;A 페이지로 즉시 대응 가능</strong></td>
</tr>
</tbody></table>
<blockquote>
<p>💬 고객사는 개발팀의 진행 상황을 실시간으로 가시화할 수 있었고,
ERP와 현업 간의 간극이 현저히 줄어들었다.</p>
</blockquote>
<h2 id="결론">결론</h2>
<p>노션은 단순한 메모 툴이 아니라,
<strong>“팀의 일하는 문화를 바꾸는 시스템”</strong>.</p>
<p>기획부터 QA까지의 모든 흐름을 문서와 데이터로 연결하고,
각자의 워크플로우를 노션이라는 한 공간에서 통합했을 때
프로젝트의 속도와 품질이 모두 올라갔다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로젝트 마일스톤 정하기 + Task 정리]]></title>
            <link>https://velog.io/@oh_yunseong/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%A7%88%EC%9D%BC%EC%8A%A4%ED%86%A4-%EC%A0%95%ED%95%98%EA%B8%B0-Task-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@oh_yunseong/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%A7%88%EC%9D%BC%EC%8A%A4%ED%86%A4-%EC%A0%95%ED%95%98%EA%B8%B0-Task-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 11 Aug 2025 12:34:42 GMT</pubDate>
            <description><![CDATA[<h3 id="📃index">[📃Index]</h3>
<ol>
<li><a href="#1%EF%B8%8F%E2%83%A3-mvp">MVP</a></li>
<li><a href="#2%EF%B8%8F%E2%83%A3-%EB%82%B4%EB%B6%80-qa-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%B6%9C%EC%8B%9C">내부 QA, 테스트 출시</a></li>
<li><a href="#3%EF%B8%8F%E2%83%A3-%EC%95%B1-%EB%B0%B0%ED%8F%AC">앱 배포 </a></li>
<li><a href="#4%EF%B8%8F%E2%83%A3-%EC%9B%B9-%EC%95%B1-%EC%B6%9C%EC%8B%9C">웹, 앱 출시</a></li>
</ol>
<hr>
<h1 id="🧭-마일스톤">🧭 마일스톤</h1>
<blockquote>
<p>⏱️ 프로젝트 종료 목표일 : <code>9월 26일</code></p>
</blockquote>
<h2 id="1️⃣-mvp">1️⃣ MVP</h2>
<blockquote>
<p>⏱️ 일정 : 1차MVP <code>8월 28일</code>, 2차MVP <code>9월 2일</code>
🔥 목표 : 핵심플로우 완성 ✚ 기본적인 사용자 경험 제공</p>
</blockquote>
<h3 id="🚀-task-1️⃣--1차mvp">🚀 Task 1️⃣ : 1차MVP</h3>
<ol>
<li><input disabled="" type="checkbox"> 게시글상세페이지 - 마감기능, 투표기능, 댓글기능 </li>
<li><input disabled="" type="checkbox"> 마이페이지 - 제작, 참여 게시글 조회</li>
<li><input disabled="" type="checkbox"> 메인페이지 - 공개 게시글 조회</li>
<li><input disabled="" type="checkbox"> 투표제작페이지 - 이미지업로드 기능, 게시글생성기능</li>
<li><input disabled="" type="checkbox"> 투표확대페이지 - 투표사진확대</li>
<li><input disabled="" type="checkbox"> 온보딩페이지 </li>
<li><input disabled="" type="checkbox"> 스플래시페이지</li>
<li><input disabled="" type="checkbox"> 투표수정페이지 - 투표수정기능
💡 2차 MVP는 완성도 ⭐️⭐️⭐️⭐️⭐️</li>
</ol>
<h3 id="🚀-task-2️⃣--2차mvp">🚀 Task 2️⃣ : 2차MVP</h3>
<ol>
<li><input disabled="" type="checkbox"> 알림페이지 - 알림 기능</li>
<li><input disabled="" type="checkbox"> 설정페이지 - 알람설정, 회원탈퇴, 로그아웃 기능</li>
<li><input disabled="" type="checkbox"> 코치마크페이지 - 사용자온보딩수행 확인</li>
<li><input disabled="" type="checkbox"> 내정보변경페이지 - 개인정보변경 기능</li>
<li><input disabled="" type="checkbox"> 이용약관,개인정보처리방침페이지
💡 2차 MVP는 완성도 ⭐️ </li>
</ol>
<hr>
<h2 id="2️⃣-내부-qa-✚-베타테스트">2️⃣ 내부 QA ✚ 베타테스트</h2>
<blockquote>
<p>⏱️ 일정 : <code>9월 7일</code>
🔥 목표 : 안정성확보 ✚ 사용자피드백 수집</p>
</blockquote>
<h3 id="🚀-task-1️⃣--내부-qa">🚀 Task 1️⃣ : 내부 QA</h3>
<ol>
<li><input disabled="" type="checkbox"> QA(버그, 핵심플로우 검증)<h3 id="🚀-task-2️⃣--사용자-테스트">🚀 Task 2️⃣ : 사용자 테스트</h3>
</li>
<li><input disabled="" type="checkbox"> 뽀또픽 테스터 재방문 및 심층 인터뷰(5인)</li>
<li><input disabled="" type="checkbox"> 원격으로 테스트 사용 권유 및 설문(20명)</li>
<li><input disabled="" type="checkbox"> 테스트 결과 취합 및 보완사항 도출(5가지)</li>
</ol>
<hr>
<h2 id="3️⃣-앱-배포">3️⃣ 앱 배포</h2>
<blockquote>
<p>⏱️ 일정 : <code>9월 10일</code>
🔥 목표 : 스토어 등록(심사대기) ✚ 초기배포 안정화</p>
</blockquote>
<h3 id="🚀-task-1️⃣--안드로이드--ios-배포-프로세스-확인">🚀 Task 1️⃣ : 안드로이드 / IOS 배포 프로세스 확인</h3>
<ol>
<li><input disabled="" type="checkbox"> Android/iOS등록(스토어 필수 항목 최소화)</li>
<li><input disabled="" type="checkbox"> 스토어 이미지/설명(ASO기본)<h3 id="🚀-task-2️⃣--앱-기능-테스트">🚀 Task 2️⃣ : 앱 기능 테스트</h3>
</li>
<li><input disabled="" type="checkbox"> 안정성 모니터링</li>
<li><input disabled="" type="checkbox"> 쌓여 있는 이슈 트래킹 및 수정 작업</li>
</ol>
<hr>
<h2 id="4️⃣-웹-앱-출시">4️⃣ 웹, 앱 출시</h2>
<blockquote>
<p>⏱️ 일정 : <code>9월 26일</code>
🔥 목표 : 데이터기반 개선 사이클 시작 ✚ 서비스 안정화</p>
</blockquote>
<h3 id="🚀-task-1️⃣--웹서비스-분석-툴">🚀 Task 1️⃣ : 웹서비스 분석 툴</h3>
<ol>
<li><input disabled="" type="checkbox"> 분석툴 1, 2개 연결</li>
<li><input disabled="" type="checkbox"> 서비스 목표 달성 지표 데이터 확인, 리뷰(KPI)<h3 id="🚀-task-2️⃣--서비스-홍보">🚀 Task 2️⃣ : 서비스 홍보</h3>
</li>
<li><input disabled="" type="checkbox"> 웹서비스 홍보용 SNS 개설 및 콘텐츠 생성</li>
<li><input disabled="" type="checkbox"> 서비스 홍보자료 제작 및 커뮤니티 배포<h3 id="🚀-task-3️⃣--운영-개선">🚀 Task 3️⃣ : 운영 개선</h3>
</li>
<li><input disabled="" type="checkbox"> 기술개선, 비즈니스 개선 항목 정리</li>
</ol>
<hr>
<blockquote>
<h4 id="✔️-참고---kpi-예시">✔️ 참고 - KPI 예시</h4>
<table>
<thead>
<tr>
<th>구분</th>
<th>지표 예시</th>
<th>기준선(목표)</th>
<th>문제</th>
<th>긍정</th>
</tr>
</thead>
<tbody><tr>
<td>유입</td>
<td>신규 방문자 수</td>
<td>X명/주</td>
<td>전주 대비 -20% 이상 감소</td>
<td>안정적 유지 또는 증가</td>
</tr>
<tr>
<td>전환</td>
<td>회원가입 전환율</td>
<td>≥ 25%</td>
<td>15% 이하</td>
<td>30% 이상</td>
</tr>
<tr>
<td>행동</td>
<td>투표 생성 수</td>
<td>X건/주</td>
<td>급감</td>
<td>꾸준한 증가</td>
</tr>
<tr>
<td>리텐션</td>
<td>D1/D7 재방문율</td>
<td>D1 ≥ 35%, D7 ≥ 15%</td>
<td>D1 &lt; 20%</td>
<td>목표 초과 유지</td>
</tr>
<tr>
<td>참여</td>
<td>평균 투표 참여 수</td>
<td>≥ 5명/투표</td>
<td>2명 이하</td>
<td>목표 이상</td>
</tr>
<tr>
<td>품질</td>
<td>오류율</td>
<td>&lt; 1%</td>
<td>지속 증가</td>
<td>0.5% 이하 유지</td>
</tr>
</tbody></table>
</blockquote>
<h4 id="✔️-주요지표">✔️ 주요지표</h4>
<ul>
<li>전환율 : 유입 이후, 회원가입/투표 생성 ▶︎ UX 문제</li>
<li>리텐션 : 콘텐츠 확대 알림, 재참여 유도 ▶︎ 기획, UX 문제 </li>
<li>참여율 : 투표 참여 분석 ▶︎ 유입과 홍보</li>
<li>오류율 : 앱 신뢰도 관리 ▶︎ 개발 문제</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[🔥 RESTful API 설계 전략과 지향점]]></title>
            <link>https://velog.io/@oh_yunseong/RESTful-API-%EC%84%A4%EA%B3%84-%EC%A0%84%EB%9E%B5%EA%B3%BC-%EC%A7%80%ED%96%A5%EC%A0%90</link>
            <guid>https://velog.io/@oh_yunseong/RESTful-API-%EC%84%A4%EA%B3%84-%EC%A0%84%EB%9E%B5%EA%B3%BC-%EC%A7%80%ED%96%A5%EC%A0%90</guid>
            <pubDate>Sat, 17 May 2025 07:02:48 GMT</pubDate>
            <description><![CDATA[<h1 id="🔥-restful-api-설계-전략과-지향점">🔥 RESTful API 설계 전략과 지향점</h1>
<h2 id="✨-서비스-개요">✨ 서비스 개요</h2>
<p>CHOOZ는 사용자가 여러 장의 사진 중 &quot;SNS에 어떤 사진을 올릴지 고민될 때&quot; 도움을 받기 위해 만든 <strong>사진 기반 투표 SNS</strong> 서비스입니다. 사진 업로드와 투표 생성, 실시간 결과 확인, 댓글 참여, 공유 등 다양한 API를 서버에서 제공합니다.</p>
<hr>
<h2 id="✅-restful-api-설계-전략">✅ RESTful API 설계 전략</h2>
<h3 id="1-도메인-중심의-리소스-설계">1. <strong>도메인 중심의 리소스 설계</strong></h3>
<ul>
<li>모든 API 엔드포인트는 도메인 개념(Post, Comment, Notification, User 등)을 중심으로 나뉘었습니다.</li>
<li><code>/api/posts</code>, <code>/api/users/me</code>, <code>/api/notifications</code> 등 리소스 경로 유지</li>
</ul>
<h3 id="2-http-method의-역할-분리">2. <strong>HTTP Method의 역할 분리</strong></h3>
<ul>
<li><code>GET</code>: 조회</li>
<li><code>POST</code>: 생성/액션 트리거</li>
<li><code>PUT</code>: 전체 수정</li>
<li><code>PATCH</code>:일부 수정</li>
<li><code>DELETE</code>: 삭제</li>
</ul>
<blockquote>
<p>예시: <code>/api/users/me/device-tokens</code> 하나의 엔드포인트에서</p>
<ul>
<li><code>POST</code>로 등록</li>
<li><code>DELETE</code>로 해제</li>
</ul>
</blockquote>
<h3 id="3-리소스-계층-구조-표현">3. <strong>리소스 계층 구조 표현</strong></h3>
<ul>
<li><code>/posts/{postId}/comments</code>, <code>/posts/{postId}/choices</code>와 같이 하위 리소스를 명확하게 표현하여 의미 전달력 강화</li>
</ul>
<h3 id="4-query-parameter-활용-최적화">4. <strong>Query Parameter 활용 최적화</strong></h3>
<ul>
<li><p>페이징, 정렬, 필터 조건은 모두 Query로 처리</p>
<ul>
<li><code>/api/posts?cursor=...&amp;size=10</code></li>
<li><code>/api/posts/{postId}/comments?page=0&amp;size=5</code></li>
</ul>
</li>
<li><p>캐싱과 브라우저 히스토리를 위해 같은 url에서 조건만 바뀌는 조회일 경우, 쿼리파라미터 적극적 활용</p>
</li>
</ul>
<h3 id="5-me-표현-도입">5. <strong>me 표현 도입</strong></h3>
<ul>
<li>사용자 ID를 알지 않고도 클라이언트에서 내 정보 처리 가능</li>
</ul>
<h3 id="6-공개범위에-따른-설계-분기">6. <strong>공개범위에 따른 설계 분기</strong></h3>
<ul>
<li><code>/posts/{id}</code>: 공개 또는 내 게시물만 접근 가능</li>
<li><code>/posts/share/{token}</code>: 난수 기반 공유 링크 접근 전용</li>
<li>서버에서는 항상 권한 검증(토큰 또는 작성자 여부)</li>
</ul>
<h3 id="7-푸시알림은-비동기-내부-트리거로-처리">7. <strong>푸시알림은 비동기 내부 트리거로 처리</strong></h3>
<ul>
<li>투표, 댓글 등의 이벤트 발생 시 서버 내부에서 알림 발송</li>
<li>REST API는 디바이스 등록, 알림 설정만 제공하고, 실제 발송은 로직에 내재화</li>
</ul>
<h3 id="8-확장-가능한-전략-패턴-구조-도입">8. <strong>확장 가능한 전략 패턴 구조 도입</strong></h3>
<ul>
<li><code>/users/me/posts?type=created</code> 등 쿼리 기반 분기는 전략 패턴으로 처리</li>
<li><code>switch-case</code>가 아닌 <code>~Strategy</code> 인터페이스로 각 타입을 유연하게 분리 → <code>OCP</code></li>
</ul>
<hr>
<h2 id="🔐-보안-설계-원칙-적용-사례">🔐 보안 설계 원칙 적용 사례</h2>
<h3 id="✅-usersme-vs-usersuserid-명확한-분리">✅ <code>/users/me</code> vs <code>/users/{userId}</code> 명확한 분리</h3>
<ul>
<li><strong>/users/me</strong>: 민감 정보 포함 가능 (이메일, 알림 설정 등)</li>
<li><strong>/users/{userId}</strong>: 프로필만 공개, 다른 유저 정보는 권한 제한 필수</li>
</ul>
<h3 id="🔍-이유-및-장점">🔍 이유 및 장점</h3>
<table>
<thead>
<tr>
<th>비교 항목</th>
<th><code>/users/me</code></th>
<th><code>/users/{userId}</code></th>
</tr>
</thead>
<tbody><tr>
<td>민감 정보 포함</td>
<td>✅</td>
<td>❌</td>
</tr>
<tr>
<td>userId 노출 필요</td>
<td>❌</td>
<td>✅</td>
</tr>
<tr>
<td>인증 기반 처리</td>
<td>✅</td>
<td>⚠️</td>
</tr>
</tbody></table>
<blockquote>
<p><code>/me</code>는 JWT 기반 사용자 판별이 확정적이므로 실수 가능성이 거의 없음
<code>/users/{userId}</code>는 보안 실수로 인해 다른 사용자의 정보가 노출될 수 있음(권한체크로직 필수)
<strong>공개된 프로필 게시물 조회시에만</strong>, <code>{userId}</code>로 접근.</p>
</blockquote>
<h3 id="예시-문제">예시 문제</h3>
<pre><code class="language-java">//내정보조회
@putMapping(&quot;/users/{userId}&quot;) 
public UserInfo getUser(@PathVariable Long userId) {
    return userRepository.findById(userId); // 권한 체크 빠짐
}</code></pre>
<h3 id="필수사항">필수사항</h3>
<ul>
<li><code>/me</code>는 민감 정보 전용(내정보조회 및 수정)</li>
<li><code>/users/{id}</code>는 공개 정보 전용(상대프로필 조회)</li>
<li>프론트엔드는 두 경로를 명확히 구분해 사용</li>
</ul>
<hr>
<h2 id="🔬-고려한-restful-설계">🔬 고려한 RESTful 설계</h2>
<ol>
<li>자원 중심(Resource-Oriented)<ul>
<li>모든 기능은 도메인 단위 리소스로 분리됨 </li>
</ul>
</li>
<li>HTTP 명세 준수<ul>
<li>메서드 별 역할 구분 (<code>GET</code>, <code>POST</code>, <code>DELETE</code>, <code>PUT</code>) </li>
</ul>
</li>
<li>URI의 일관성과 명확성<ul>
<li><code>/posts/{id}</code> vs <code>/posts/share/{token}</code> 구분 명확 </li>
</ul>
</li>
<li>무상태성(Stateless)<ul>
<li>JWT 토큰으로 인증, 요청마다 독립 처리                       </li>
</ul>
</li>
<li>클라이언트-서버 분리<ul>
<li>프론트는 상태만 관리, 백엔드는 명확한 API 응답 설계 </li>
</ul>
</li>
<li>캐시 가능성 <ul>
<li>GET 요청 중심의 리스트 조회는 캐싱 가능성 고려함</li>
</ul>
</li>
</ol>
<hr>
<h2 id="✨-마무리">✨ 마무리</h2>
<p>CHOOZ의 API는 단순한 CRUD 설계가 아닌, <strong>도메인 구조에 기반한 명확하고 RESTful한 설계 전략</strong>을 만드는 것이 목표. 
<strong>유지보수성, 확장성, 사용자경험</strong>과 관련된 효율적인 API설계 필요.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[나만의 웹 이력서 만들기(1) Next.js + Tailwind + Vercel]]></title>
            <link>https://velog.io/@oh_yunseong/%EB%82%98%EB%A7%8C%EC%9D%98-%EC%9B%B9-%EC%9D%B4%EB%A0%A5%EC%84%9C-%EB%A7%8C%EB%93%A4%EA%B8%B01-Next.js-Tailwind-Vercel</link>
            <guid>https://velog.io/@oh_yunseong/%EB%82%98%EB%A7%8C%EC%9D%98-%EC%9B%B9-%EC%9D%B4%EB%A0%A5%EC%84%9C-%EB%A7%8C%EB%93%A4%EA%B8%B01-Next.js-Tailwind-Vercel</guid>
            <pubDate>Tue, 25 Mar 2025 23:58:39 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>웹 개발 입문자도 따라할 수 있는 웹이력서 만들기</p>
</blockquote>
<hr>
<h2 id="🚀-목표">🚀 목표</h2>
<ul>
<li>Next.js + TypeScript로 웹 이력서 프로젝트 만들기</li>
<li>Tailwind CSS로 디자인 적용</li>
<li>GitHub에 코드 업로드</li>
<li>Vercel에 무료로 배포하기</li>
</ul>
<hr>
<h2 id="🧱-1단계-개발-환경-준비">🧱 1단계: 개발 환경 준비</h2>
<h3 id="🔧-필수-설치-항목">🔧 필수 설치 항목</h3>
<ul>
<li><a href="https://nodejs.org/">Node.js</a> (최소 v18 이상 권장)</li>
<li>코드 에디터: <a href="https://code.visualstudio.com/">Visual Studio Code</a> 또는 IntelliJ</li>
</ul>
<h3 id="✅-nodejs-설치-확인">✅ Node.js 설치 확인</h3>
<p>터미널 또는 명령 프롬프트에서 아래 명령어 입력:</p>
<pre><code class="language-bash">node -v
npm -v</code></pre>
<p>문제 없이 버전이 출력되면 설치 완료!</p>
<hr>
<h2 id="⚙️-2단계-nextjs-프로젝트-생성">⚙️ 2단계: Next.js 프로젝트 생성</h2>
<pre><code class="language-bash">npx create-next-app@latest resume --ts</code></pre>
<p><strong>설정 질문이 나오면 이렇게 답변하세요:</strong></p>
<table>
<thead>
<tr>
<th>질문</th>
<th>답변</th>
</tr>
</thead>
<tbody><tr>
<td>Would you like to use ESLint?</td>
<td>Yes (Enter)</td>
</tr>
<tr>
<td>Would you like to use Tailwind CSS?</td>
<td>Yes</td>
</tr>
<tr>
<td>Would you like to use the <code>src/</code> directory?</td>
<td>Yes</td>
</tr>
<tr>
<td>Would you like to use the App Router?</td>
<td>Yes</td>
</tr>
<tr>
<td>Would you like to customize the default import alias?</td>
<td>No (Enter)</td>
</tr>
<tr>
<td>Would you like to use Turbopack?</td>
<td>No (Enter) 또는 Yes (아래 참고)</td>
</tr>
</tbody></table>
<blockquote>
<p>💡 참고: Turbopack은 최신 번들러지만 불안정하다. 문제가 생기면 <code>--turbo=false</code> 옵션을 추가하여 Webpack으로 실행!</p>
</blockquote>
<hr>
<h2 id="📁-3단계-프로젝트-구조-이해">📁 3단계: 프로젝트 구조 이해</h2>
<pre><code class="language-bash">resume/
├── src/
│   ├── app/            # 페이지 구조 (App Router)
│   ├── components/     # 재사용 컴포넌트 보관
│   └── styles/         # 전역 CSS (globals.css)
├── public/             # 이미지 등 정적 파일
├── tailwind.config.js  # Tailwind 설정
├── postcss.config.js   # PostCSS 설정
├── tsconfig.json       # TypeScript 설정
└── package.json        # 프로젝트 메타 정보</code></pre>
<hr>
<h2 id="🎨-4단계-tailwindcss-적용-확인-및-설정">🎨 4단계: TailwindCSS 적용 확인 및 설정</h2>
<h3 id="1-globalscss-수정-srcappglobalscss">1. <code>globals.css</code> 수정 (src/app/globals.css)</h3>
<pre><code class="language-css">@tailwind;

:root {
  --background: #ffffff;
  --foreground: #171717;
}

body {
  background: var(--background);
  color: var(--foreground);
  font-family: Arial, Helvetica, sans-serif;
}</code></pre>
<h3 id="2-tailwindconfigjs">2. <code>tailwind.config.js</code></h3>
<pre><code class="language-js">module.exports = {
  content: [
    &#39;./src/app/**/*.{js,ts,jsx,tsx}&#39;,
    &#39;./src/components/**/*.{js,ts,jsx,tsx}&#39;,
  ],
  theme: {
    extend: {
      colors: {
        primary: &#39;#facc15&#39;,   // 노란색
        secondary: &#39;#a855f7&#39;, // 보라색
      },
    },
  },
  plugins: [],
};</code></pre>
<h3 id="3-postcssconfigjs">3. <code>postcss.config.js</code></h3>
<pre><code class="language-js">module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};</code></pre>
<blockquote>
<p>또는 Turbopack을 사용할 경우:</p>
</blockquote>
<pre><code class="language-js">// postcss.config.mjs
const config = {
  plugins: [&quot;@tailwindcss/postcss&quot;],
};
export default config;</code></pre>
<hr>
<h2 id="🧪-5단계-개발-서버-실행">🧪 5단계: 개발 서버 실행</h2>
<pre><code class="language-bash">cd resume
npm install
npm run dev</code></pre>
<blockquote>
<p>브라우저에서 <code>http://localhost:3000</code> 접속</p>
</blockquote>
<hr>
<h2 id="🌐-6단계-github에-올리기">🌐 6단계: GitHub에 올리기</h2>
<h3 id="1-gitignore-확인">1. <code>.gitignore</code> 확인</h3>
<p><code>.gitignore</code> 파일에 다음 항목이 포함되어야 함:</p>
<pre><code>node_modules/
.next/
dist/
.env</code></pre><h3 id="2-github-연결-및-업로드">2. GitHub 연결 및 업로드</h3>
<pre><code class="language-bash">git init
git remote add origin https://github.com/your-username/resume.git
git add .
git commit -m &quot;first commit&quot;
git push -u origin main</code></pre>
<blockquote>
<p>❗ GitHub에서 레포지토리 먼저 생성</p>
</blockquote>
<hr>
<h2 id="🚀-7단계-vercel에-배포하기">🚀 7단계: Vercel에 배포하기</h2>
<h3 id="1-vercelcom-접속">1. <a href="https://vercel.com">vercel.com</a> 접속</h3>
<ul>
<li>GitHub 계정으로 로그인</li>
<li><strong>&quot;Add New Project&quot; → **</strong><code>resume</code><strong>** 선택 → Import</strong></li>
<li>설정은 기본값 유지</li>
<li><code>Environment Variables</code>는 생략가능(지금은 필요 없음)</li>
<li><strong>Deploy 클릭!</strong></li>
</ul>
<h3 id="2-배포-완료-🎉">2. 배포 완료 🎉</h3>
<ul>
<li><code>https://your-project-name.vercel.app</code> 주소 생성됨</li>
<li>이후 GitHub에 푸시만 해도 Vercel이 자동 재배포 💫</li>
</ul>
<hr>
<h2 id="🎯-마무리">🎯 마무리</h2>
<p>이제 <strong>웹 이력서</strong> 공개! 간단하게 프로젝트 생성 &gt; 배포까지!
내용 채워서 웹 링크로 지원~ ㅎㅎ
Next.js배우면서 spa 구조도 알아보면서 해봐야겠다!</p>
<h2 id="⛳️action-item">⛳️Action Item</h2>
<ul>
<li>Tailwind로 예쁘게 스타일링하기</li>
<li>섹션별 컴포넌트로 나누기 (프로필, 경력, 기술 스택 등)</li>
<li>도메인 연결하고 완성도 올리기</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[피그마]피그마 기초]]></title>
            <link>https://velog.io/@oh_yunseong/%ED%94%BC%EA%B7%B8%EB%A7%88%ED%94%BC%EA%B7%B8%EB%A7%88-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@oh_yunseong/%ED%94%BC%EA%B7%B8%EB%A7%88%ED%94%BC%EA%B7%B8%EB%A7%88-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Sat, 22 Mar 2025 15:25:46 GMT</pubDate>
            <description><![CDATA[<h2 id="레이어와-그룹">레이어와 그룹</h2>
<p>점선 네모 - 그룹
그룹하는 방법 command + g
그룹해지 방법 command + shift+ g
레이어 층의 개념
앞과 뒤 z축 그래서 순서에 따라 보이는 것이 다르다. 
레이어 앞으로 가기 command + [
레이어 뒤로 가기 command + ]
레이어 맨 앞, 뒤로 가기 command + option [, ]
요소 복제
option + drag</p>
<h2 id="프레임">프레임</h2>
<p>ui 작업을 위한 틀
하나의 화면을 구성하는 틀
단축키 f + freeset고르기
프레임 안에 프레임을 넣을 수도 있음</p>
<h2 id="섹션">섹션</h2>
<p>여러화면에 대한 프레임을 그룹화할 수도 있음
ex) 시안1, 2
shift + s
option + 커서 이동하면 간격을 다 알아볼 수 있음
페이지
페이지는 여러개로 나눌 수 있다.</p>
<h2 id="레이아웃-그리드">레이아웃, 그리드</h2>
<p>웹 : 12단 그리드
모바일 : 4, 6단 그리드
column, gutter, margin
grid : 작은요소
columns : 전체 레이아웃
shift + g : 레이아웃 그리드를 보였다 안보였다 할 수 있음</p>
<h2 id="도형">도형</h2>
<p>appearance 
fill : 배경 컬러, 이미지 삽입, 비디오 삽입(미리보기 시) 
    fit으로 하면 비율에 맞춰서 작악짐
    crop : 잘라서 원하는 부분만 보여줄 수 있음
    tile : 원본크기에 맞춰서 반복
remove background : 배경 없어짐
fill, stroke 안에서도 레이어를 조절 할 수 있음
line모양도 설정할 수 있음
effects
    inner shadow 
    drop shadow 
    background blur : 레이어를 활용해 블러처리 시키고 싶은 이미지를 뒤로 보냄, 투명도를 넣어줘야 함 
text : 미래의 건강 산업, 개인 맞춤형 의료
layout에서 truncate text - inabled 처리  auto height로 변경하면 
max lines 설정에 따라 ...표시가 됨 
command + option + g 하면 프레임으로 묶여짐
command + option + c 하면 속성을 복사 할 수 있음
clip content radius 맞춰줌</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[뽀또픽👇]👀2차 팀 협업정책🥰]]></title>
            <link>https://velog.io/@oh_yunseong/%EB%BD%80%EB%98%90%ED%94%BD2%EC%B0%A8-%ED%8C%80-%ED%98%91%EC%97%85%EC%A0%95%EC%B1%85</link>
            <guid>https://velog.io/@oh_yunseong/%EB%BD%80%EB%98%90%ED%94%BD2%EC%B0%A8-%ED%8C%80-%ED%98%91%EC%97%85%EC%A0%95%EC%B1%85</guid>
            <pubDate>Fri, 21 Mar 2025 01:37:41 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>팀 운영에서 소통하는데 어려움이 적었지만, 없던 것은 아니었다.
업무소통하고 트래킹 하는 것은 시스템 없이도 노력과 시간을 투자하면 충분히 가능하다. 
하지만, 팀원 모두 주어진 시간과 에너지는 정해져 있기 때문에 결국 다른 부분에서 손해로 이어졌다.
개개인의 성향을 적극적으로 만들거나, 팀에 필요한 부분에 자발적으로 기여하게 하는 것은 불가능하다. 
결국에는 협업하는데에 있어서 시스템을 만드는 것이 굉장히 중요하다는 생각을 했다.</p>
</blockquote>
<h2 id="📌-기록보관소archive">📌 기록보관소(Archive)</h2>
<h3 id="📌-노션">📌 노션</h3>
<ul>
<li><u><strong><em>기록보관용</em></strong></u></li>
<li>실시간 협업 가능(생성, 편집)</li>
<li>데이터베이스(보드, 칸반, 캘린더 등 다양한 보기형식 지원)</li>
<li>wiki형태로 변환 가능<h2 id="📌-프로젝트관리project-planning">📌 프로젝트관리(Project Planning)</h2>
<h3 id="📌-디스코드">📌 디스코드</h3>
</li>
<li><u><strong><em>회의, 업무대화용</em></strong></u></li>
<li>프로젝트 논의 : 전체 채팅방 </li>
<li>기술 논의 : 각 기술 분야 채팅방 </li>
<li>스프린트(MVP) 논의 : 기획/디자인 채팅방</li>
</ul>
<h3 id="📌-트렐로">📌 트렐로</h3>
<ul>
<li><u><strong><em>업무할당 및 진행상황 추적용</em></strong></u></li>
<li>칸반형태의 업무 체크리스트</li>
<li>각 업무에 대해 전체적인 파악이 바로 가능</li>
<li>디스코드와 연동하여 대화하며 추가 및 수정 가능</li>
</ul>
<h3 id="📌-카카오-오픈채팅방--챗봇">📌 카카오 오픈채팅방 + 챗봇</h3>
<ul>
<li><u><strong><em>일상대화용</em></strong></u></li>
<li>디자인 및 개발을 하지 않는 순간에도 꾸준히 프로덕트 관련 정보 제공</li>
<li>그라운드룰, 정책 등과 관련된 내용을 미리 작성후 제공</li>
<li>이동 중에 가볍게 정보확인 가능한 용으로 사용<h2 id="📌-제품개발product-development">📌 제품개발(Product Development)</h2>
<h3 id="📌-피그마">📌 피그마</h3>
</li>
<li>서비스 컨셉</li>
<li>서비스 흐름</li>
<li>UX리서치 </li>
<li>UI디자인 </li>
<li>와이어프레임</li>
<li>프로토타입</li>
<li>UT</li>
<li>MVP기획</li>
<li>기능명세 </li>
<li>🚨<strong><em>코멘트 달기 👉 회의 때 전 인원 점검 및 피드백</em></strong>🚨</li>
<li>🚨<strong><em>문서 작성 완료 👉 회의 때 전 인원 점검 및 피드백</em></strong>🚨<h3 id="📌-git--github">📌 Git &amp; Github</h3>
</li>
<li>웹서비스 코드 개발 및 서비스 구현</li>
<li>PR을 통해 개발 내역 confirm</li>
<li>워크플로우 작성<ul>
<li>PR - Preview - dev - prd 시스템 구축</li>
</ul>
</li>
</ul>
<h2 id="📌-기대하는-업무진행-플로우">📌 기대하는 업무진행 플로우</h2>
<ol>
<li>디스코드로 회의 및 업무대화 진행 sub(카카오 오픈채팅)</li>
<li>트렐로로 업무할당(기획, 디자인 위주) </li>
<li>피그마로 제품 개발 sub(카카오 오픈채팅)</li>
<li>디스코드로 회의 및 업무대화 진행 + <strong>피그마 점검 및 피드백</strong> sub(카카오 오픈채팅)</li>
<li>트렐로로 업무할당(개발 위주)</li>
<li>서비스개발</li>
<li>Preview를 보며, 1 ~ 6 or 3 ~ 6반복</li>
</ol>
<h2 id="📌-보완사항">📌 보완사항</h2>
<ol>
<li>국소적으로 발생하는 기획 건을 전부 소통할 수 있도록 오픈채팅방을 사용</li>
<li>대화했던 내용을 바로 업무리스트로 옮기기 위해 디스코드with트렐로 활용</li>
<li>Preview 배포를 통해 디자인과 프론트의 단차 제거</li>
<li>피그마 코멘트 후 회의를 통해 전 인원 기능 및 디자인 점검 및 피드백 👉 최종 락인하여 기능 범위와 일정의 애매함 해소 </li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[뽀또픽]KPT 회고 진행! 어떻게 진행 했을까?, 우리팀에 왜 필요했을까?]]></title>
            <link>https://velog.io/@oh_yunseong/%EB%BD%80%EB%98%90%ED%94%BDKPT-%ED%9A%8C%EA%B3%A0-%EC%A7%84%ED%96%89-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%A7%84%ED%96%89-%ED%96%88%EC%9D%84%EA%B9%8C-%EC%9A%B0%EB%A6%AC%ED%8C%80%EC%97%90-%EC%99%9C-%ED%95%84%EC%9A%94%ED%96%88%EC%9D%84%EA%B9%8C</link>
            <guid>https://velog.io/@oh_yunseong/%EB%BD%80%EB%98%90%ED%94%BDKPT-%ED%9A%8C%EA%B3%A0-%EC%A7%84%ED%96%89-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%A7%84%ED%96%89-%ED%96%88%EC%9D%84%EA%B9%8C-%EC%9A%B0%EB%A6%AC%ED%8C%80%EC%97%90-%EC%99%9C-%ED%95%84%EC%9A%94%ED%96%88%EC%9D%84%EA%B9%8C</guid>
            <pubDate>Thu, 20 Mar 2025 02:52:55 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>공식적으로 프로젝트 하기로 한 6주가 지나갔다.
빠듯한 기간동안 좋았던 점도 많았고, 보완해야할 부분도 많았다.
팀원 한분이 KPT회고라는걸 알려줬다. 이 회고를 통해 교훈을 얻으며 끝 매듭을 짓고, 새로운 시작을 준비할 수 있었다. </p>
</blockquote>
<h1 id="📌-kpt-회고란">📌 KPT 회고란?</h1>
<p>Keep, Problem, Try의 약자로 Keep은 잘 한 것, Problem은 아쉬운 것, Try는 K와 P 기반으로 실행가능하고, 측정가능한 Action을 도출.</p>
<p><strong>회고의 목적</strong></p>
<p>지금보다 더 나은 협업을 위해 실행할 action item을 2~3가지 정도 도출해내기.</p>
<p><strong>회고의 시기</strong></p>
<p>kpt회고를 해보면서, 이 회고방식을 도입하는 시기가 중요하다고 느꼈다. 6주동안의 빠듯한 프로젝트가 끝나고, 새로운 시작을 준비해야했기 때문에 지금 한번 잡고 가는건 좋은 것 같았다.</p>
<p><strong>회고의 구성</strong></p>
<ol>
<li>Keep (유지할 것)</li>
</ol>
<ul>
<li>이번 스프린트(또는 프로젝트)에서 잘된 점</li>
<li>효과적이었던 방법이나 프로세스</li>
<li>계속 유지하고 싶은 팀의 문화나 협업 방식</li>
</ul>
<ol start="2">
<li>Problem (문제점)</li>
</ol>
<ul>
<li>비효율적이었던 점이나 개선이 필요한 요소</li>
<li>업무 진행 중 겪은 어려움</li>
<li>팀 내 의사소통이나 도구 사용의 문제</li>
</ul>
<ol start="3">
<li>Try (시도할 것)</li>
</ol>
<ul>
<li>Problem에서 도출된 개선안을 구체적으로 실천할 방법</li>
<li>다음 스프린트나 프로젝트에서 적용할 변화</li>
<li>새로운 도구나 프로세스를 도입할 아이디어</li>
</ul>
<h2 id="📌-진행-방식">📌 진행 방식</h2>
<ol>
<li>KEEP/PROBLEM은 각자 미리 작성해오기</li>
<li>회고 미팅에서 동료들에게 내용 공유<ul>
<li>회고 미팅에서는 노션을 활용해 함께 브레인 스토밍하며 진행했다. Keep, Problem을 이야기 하면서 Try부분을 함께 적었다.</li>
</ul>
</li>
<li>다같이 TRY와 Action Item을 추출하기<ul>
<li>도출된 Try항목 중에서 우선순위를 정했다.</li>
<li>Try에서 적었던 내용들을 구체적이고 실행가능한 Action Item으로 정리했다.</li>
<li>다음 회고 때는 이 Action Item들이 잘 적용됐는지 점검하자.</li>
</ul>
</li>
</ol>
<h3 id="📌-작성-시-고려할-것들">📌 작성 시 고려할 것들</h3>
<ul>
<li>기획, 디자인, 개발 협업관계</li>
<li>서버 ↔ 클라이언트간 협업관계</li>
<li>스스로 깨달은점</li>
<li>다른사람에게 주고 싶은 피드백</li>
<li>Problem은 특정 사람을 탓하고, 비난하기 위한 항목이 아님. 대상이 사람이 아닌 현상이면 좋아요!<ul>
<li>발생했던 문제가 왜 발생했는지를 파악하고, 앞으로 일어나지 않도록 개선해나가기 위한 과정이라고 생각해주세요!</li>
</ul>
</li>
</ul>
<h1 id="📌-뽀또픽-kpt회고">📌 뽀또픽 KPT회고</h1>
<h2 id="📌-k---keep">📌 K - Keep</h2>
<h3 id="📌-1-주기적인-회의-기획-단계부터-전원이-적극적으로-참여">📌 1. 주기적인 회의, 기획 단계부터 전원이 적극적으로 참여</h3>
<ul>
<li>주기적인 회의를 통해 기획, 디자인, 개발 모두가 기획 단계부터 함께 참여.</li>
<li>이로 인해 팀원 모두가 서비스의 전반적인 흐름과 방향성을 명확히 이해할 수 있었고, 서비스에 대한 오너십이 자연스럽게 생겼다.</li>
<li>지금과 같이 기획에 적극적으로 참여하려는 마인드를 가지고 이어가면 될 것 같습니다.<h3 id="📌-2-2차-mvp-때부터-technical-document를-작성하여-개발-간-싱크를-맞췄던-것">📌 2. 2차 MVP 때부터 Technical Document를 작성하여 개발 간 싱크를 맞췄던 것</h3>
</li>
<li>개발 전에 문서화를 통해 싱크를 맞추어 개발 기간 동안의 소통 비용이 확실하게 줄었다.</li>
<li>스펙이 이미 논의되어서, MSW를 통해 api 개발에 병목 없이 클라이언트 개발이 가능했다.<ul>
<li>특히, 2차 MVP 몇몇 기능은 서버 개발 이전에 클라이언트 개발이 끝났었음</li>
</ul>
</li>
<li>앞으로도 개발 이전에 Technical Document를 통한 싱크를 컨벤션으로 가져간다!<h3 id="📌-3-의견-있으면-숨김-없이-제안했던-것">📌 3. 의견 있으면 숨김 없이 제안했던 것</h3>
</li>
<li>개발이나 디자인이나 생각한 것과 다른 부분이 있으면 의견을 제안한 것</li>
<li>그 제안을 통해서 내용이 바뀌는 것을 떠나서 이런 과정을 통해 서로의 의견을 맞추는 것이 좋았다</li>
<li>지금처럼만 해도 의견을 모으는데 부족함 없을 것 같다<h3 id="📌-4-팀-구성원의-목표와-상황을-인지하고-있던-것">📌 4. 팀 구성원의 목표와 상황을 인지하고 있던 것</h3>
</li>
<li>프로젝트 시작 전에 목표를 공유하여, 프로젝트 중에 목표에 어긋나지 않도록 동기부여할 수 있었음</li>
<li>개인적인 하루일과를 알고 있어서, 감안하여 일정을 조율할 수 있어 좋았음</li>
<li>좀 더 친해지면 좋을 것 같다(너무 친하면 안되지만…)<h3 id="📌-5-mvp를-쪼개어-진행한-것피드백">📌 5. MVP를 쪼개어 진행한 것(피드백)</h3>
</li>
<li>MVP 작게 만들고 사용자 피드백을 받아, 우선순위를 정할 수 있었다.</li>
<li>의견이 분분할 때, 사용자 피드백은 가불기였다</li>
<li>이를 바탕으로 개선해나가는 작업이 결국 프로덕트를 만드는 궁극적인 이유와도 같았다.</li>
<li>피드백 받은 것을 문서화 하여 깔끔하게 정리해야 된다. (피드백에 대한 서로의 생각 공유)</li>
<li>피드백은 아주 소중하고 두고두고 봐줘야할 요소인데, 피드백 공유에 참여한 인원이 없어 그냥 넘어갔다.</li>
<li>만약에 문서화를 한다면 같이 보면서 기획의 시간을 가졌으면 좋겠다.<h3 id="📌-6-자발적인-개발과-디자인">📌 6. 자발적인 개발과 디자인</h3>
</li>
<li>문제가 될 수 있는 부분을 자발적으로 캐치하여 개발해주신 덕분에 좋았다.</li>
<li>서버문제, 확장자 문제, UI, 캐릭터, 기능 예외 등등</li>
<li>문제가 될 부분을 미리 알려주고 진행하면 더 좋을 것같다.</li>
<li>그리고 문제가 될 부분을 알게 되면 문서화 하여 팀 전체에 공유 해야겠다. → 일정파악 / 우선순위 파악<h3 id="📌-7-매주-만나고-매주-2회씩-회의">📌 7. 매주 만나고, 매주 2회씩 회의</h3>
</li>
<li>회의 참석률이 너무 높아서 좋았다. 의견공유 활발</li>
</ul>
<h2 id="p---problem">P - Problem</h2>
<h3 id="📌-1-1차-mvp-배포된-이후에-디자이너가-원하는-디자인이-적용되지-않음을-알았다">📌 1. 1차 MVP 배포된 이후에 디자이너가 원하는 디자인이 적용되지 않음을 알았다.</h3>
<ul>
<li>QA를 위해 1차 MVP 배포를 했는데, 그때서야 디자인이 의도한대로 나오지 않았음을 알았음</li>
<li>1차적으로는 프론트엔드 팀에서 디자이너에게 결과물을 미리미리 공유하지 못한 점이 아쉬웠다.</li>
<li>Preview를 통해 미리 보여드렸다면 진작에 소통해서 해결할 수 있는 문제라 더욱 아쉬웠다.</li>
<li>가이드를 잘못줬다.</li>
<li>PR (개발) 커밋 → 리뷰 → Dev Deploy (기획, 디쟌)<h3 id="📌-2-게스트-로그인-개발이-예상한-시간보다-오래-걸렸다">📌 2. 게스트 로그인 개발이 예상한 시간보다 오래 걸렸다.</h3>
</li>
<li>게스트 로그인 개발 시간을 하루 미만으로 예상했는데, 2~3일이 지나도 해결되지 않았다.</li>
<li>2~3일이 밀렸는데, 팀에게 상황이 공유되지 않았다.</li>
<li>프론트 내에서 내가 맡은 작업은 개인적으로 책임감을 가지고 충분히 고민하여 해결하면 된다고 생각했지만, 이를 팀에게 충분히 공유하지 않고 스스로 고민하고 해결하려다 보니 결국 시간이 지체됨. 조직 전체 일정과 협업에 영향을 미친다는 점을 간과함.</li>
<li>작업 상황을 공유하며 백엔드 팀과 정리하고 공유드린 부분들을 이해하셨다고 생각했지만, 이후 보니 내 설명이 충분히 전달되지 않았고, 정확히 이해하지 못하신 부분이 있었음.</li>
<li>작업 상황과 문제를 설명할 때, 배경과 맥락을 구체적으로 전달하지 못함.<h3 id="📌-3-기능명세가-나온-이후에-의견들을-제시하여-두-번-일하게-되었다">📌 3. 기능명세가 나온 이후에 의견들을 제시하여 두 번 일하게 되었다.</h3>
</li>
<li>기능명세가 이미 완성되었는데, 그 이후에 개발자들이 개발을 진행하면서 기획 쪽에 의견들을 계속 제시했다.</li>
<li>N차 MVP 범위가 정해졌는데 새로운 기능(버그 픽스 x)을 추가하여 범위가 늘어났다</li>
<li>명세 완성 이후 팀 전체가 싱크 맞추는 기회가 없었음</li>
<li>당연히 개발자들이 명세를 보면서 미리미리 의견을 제시하면 좋겠지만, 개발을 시작할 때 명세를 보기 시작한 것도 문제라고 생각<h3 id="📌-4-개발자들의-개발-진행-상황-트래킹이-잘-안-되었다">📌 4. 개발자들의 개발 진행 상황 트래킹이 잘 안 되었다.</h3>
</li>
<li>개발자들의 개발 진행 상황 트래킹이 잘 되지 않았음</li>
<li>데일리 스크럼, 기획자 분께서 개인 연락으로 트래킹을 열심히 시도해주셨으나, 팀에게 잘 공유는 안되었음</li>
<li>1차적으로는 개발자 개인이 매일매일 진행상황을 공유했으면 해결됐을 문제</li>
<li>회사가 아니니 어쩔 수 없는 것은 인지하고 있다.<h3 id="📌-5-일부-팀원들이-논의한-상황이-팀-전체에게-공유되지-않고-반영된-경우들이-있었다">📌 5. 일부 팀원들이 논의한 상황이 팀 전체에게 공유되지 않고 반영된 경우들이 있었다.</h3>
</li>
<li>정기회의 이외에 일부 팀원들의 논의를 통해 결정된 사항들이 팀 전체에 공유가 잘 되지 않았음</li>
<li>일부 팀원들의 논의를 통해 결정된 사항들을 피그마에 잘 기술해주셨으나, diff를 파악하기 어려워서 개발자 입장에서 어느 순간 반영된 것처럼 느껴질 수 있을 것 같음</li>
<li>개발자들이 개발을 시작한 이후에나 왜 바뀌었는지에 대한 히스토리를 파악하게 됨<h3 id="📌-6-개개인의-회의-준비-부족-→-회의-지연">📌 6. 개개인의 회의 준비 부족 → 회의 지연</h3>
</li>
<li>회의 시작 전 어떤 내용을 논의해야 할지 파악되지 않아, 회의 자리에서 문제를 인식하고 바로 해결법을 찾는 형식으로 회의 진행.</li>
<li>논의에 대한 의견이 생각나지 않은 경우엔 의견을 피력하지 못하거나 고민하는 시간이 길어져 회의 시간이 불필요하게 지체됨.</li>
<li>회의 안건이 사전에 명확히 공유되지 않았음.</li>
<li>프로젝트 데드라인으로 인해 팀원들이 회의 전에 충분히 고민할 시간적 여유가 부족했음.<h3 id="📌-7-디자인-수정이-빈번히-발생함">📌 7. 디자인 수정이 빈번히 발생함.</h3>
</li>
<li>UI 작업을 완료 했으나, 이후 추가 수정 요청이 발생해 동일한 작업을 여러 번 반복해야 했음. 
→ 시간 지체</li>
<li>예상 가능한 수정 사항에 대한 사전 협의 부족.<h3 id="📌-8-기획-변경-사항-공유-부족--피그마-반영">📌 8. 기획 변경 사항 공유 부족 (+ 피그마 반영)</h3>
</li>
<li>마감 기한을 맞추기 위해 최소 기능으로 조정한 내용이 피그마에 반영되지 않음. (피그마에는 2차 MVP 수정 사항이 반영되지 않은 채로 초기 디자인 작성 후 업데이트 되지 않아 업데이트 된 점을 인지 하지 못한 상태로 UI 반영, 추후 다시 롤백)</li>
<li>개발 중 기획 변경 사항 파악이 어려워 혼란 발생.</li>
<li>소수 인원 회의로 결정된 내용이 전체 팀원에게 충분히 공유되지 않음.</li>
<li>피그마/노션 등 협업 툴에 변경 사항 기록 미흡.<h3 id="📌-9-공통-컴포넌트-정의와-소통-부족">📌 9. 공통 컴포넌트 정의와 소통 부족</h3>
</li>
<li>1차에서 투표 상세 페이지에만 있던 vertical Ellipsis가 2차에서 홈에도 추가.</li>
<li>초기 공통 컴포넌트로 지정하지 못한 채 기존에 사용하던 것들을 리팩토링 하다보니 시간 지체.</li>
<li>재사용 시도 실패 → 개발 시간이 불필요하게 지연. 홈에서는 결국 vertical Ellipsis 없는 상태로 배포.</li>
<li>디자인 단계에서 해당 컴포넌트의 재사용 가능성, 공통화 여부에 대한 논의 부족.</li>
<li>컴포넌트가 추가되는 시점에서 개발자-디자이너 간 협의 없이 바로 적용 시도.<h3 id="📌-10-케이스별-화면-ui-부족">📌 10. 케이스별 화면 UI 부족</h3>
</li>
<li>기능 명세서 및 피그마 UI가 케이스별 흐름, 예외 상황이 충분히 명확하게 정리되지 않아 UI를 구현할 때 예상하여 구현하거나 재차 확인해야 하는 상황 발생.</li>
<li>초기 기획에서 케이스별로 깊게 고민해볼 시간 부족.</li>
<li>케이스 별 UI가 피그마에 명확하게 정리되어있지 않은 경우가 있었음.<h3 id="📌-11-ux-완성도-미흡">📌 11. UX 완성도 미흡</h3>
</li>
<li>1차 MVP 이후 UX/UI 피드백 다수</li>
<li>초기 ux 설계가 미흡했음<h3 id="📌-12-협업-툴-사용-어려움">📌 12. 협업 툴 사용 어려움</h3>
</li>
<li>개발 체크리스트를 놓치는 경우가 많음<h3 id="📌-13-이유를-말하지-않고-주장을-어필했다">📌 13. 이유를 말하지 않고 주장을 어필했다.</h3>
</li>
<li>건 주고 받을 때, <del>했음 좋겠다. 이거 있어야된다. ~</del>왜하냐, ~~굳이 해야되냐?라고 하니 생각을 알 수 없어 답답했다.<h3 id="📌-14-체크리스트의-부재">📌 14. 체크리스트의 부재</h3>
</li>
<li>명세만 있고, 완료여부를 모르겠음</li>
<li>할일 목록이 없음<h3 id="📌-15-그라운드-룰-안지킴">📌 15. 그라운드 룰 안지킴</h3>
</li>
<li>그라운드룰을 잘 안보고 잘 안지키게 됨(전원)</li>
<li>그라운드룰이 실상 막 중요한 느낌은 아닌데, 지나고보면 좀 중요한듯.</li>
<li>추가할만한 룰이 있는데, 어차피 안보니까 잘안하게됨<h3 id="📌-16-답답할때-빠르게-의견공유-안함">📌 16. 답답할때, 빠르게 의견공유 안함</h3>
</li>
<li>생각 달라서 지연됨</li>
<li>생각이 다른데, 말을 안했음</li>
<li>사람한테 싫은소리하게 될까봐 싫어서 안함<h3 id="📌-17-연락-및-트래킹-시간-축소-필요">📌 17. 연락 및 트래킹 시간 축소 필요</h3>
</li>
<li>명세나 문서화, 추후 기획과 정의작업이 연락하고 트래킹하는데 시간 다써서 넘 힘들었음</li>
<li>그러다 보니 나중엔 명세에 있었는지, 얘기가 정확히 됐는지 잘 모르겠음</li>
<li>아주 보잘 것 없는 질문이어도 답변을 해줘야될 것 같음.</li>
<li>확인 하라는 공지에 확인 후 피드백이 적었다.</li>
<li>완료건, 진행 건 혹은 답답하고 이해안되는 건 등등 서로 미루지 않고 바로 말하기<h3 id="📌-18-기획-방향이-명확하지-못했음">📌 18. 기획 방향이 명확하지 못했음</h3>
</li>
<li>기획과 결과의 차이 발생<h3 id="📌-19-api-설계를-다같이-안-해서-수정이-자주-일어남">📌 19. api 설계를 다같이 안 해서 수정이 자주 일어남</h3>
</li>
<li>api 개발을 다같이 안 해서 계속 수정이 발생함</li>
<li>백에서 임의로 만들고 프런트에서 보면서 부족한 부분 수정 요청하는 방식으로 개발됨</li>
<li>백에서 프런트가 어떤 데이터를 필요로 하는지 완벽하게 알기가 힘듬<h3 id="📌-20-기능-구현-기간을-애매하게-정함">📌 20. 기능 구현 기간을 애매하게 정함</h3>
</li>
<li>기능 구현을 할 때, 정확한 기간을 정하지 않아서 기획/개발자 간의 싱크가 맞지 않았음</li>
<li>그래서 누구는 언제까지 구현되어야 한다 하고, 누구는 더 우선인 기능을 구현하고 나서 작업해야한다는 등</li>
<li>기능 별로 완료 기간을 정확하게 정하지 않고, 각자 생각하는 방향과 그림이 다름<h3 id="📌-21-성능이-너무-안나옴">📌 21. 성능이 너무 안나옴</h3>
</li>
<li>돈이 듦<h2 id="📌-t---try">📌 T - Try</h2>
<h3 id="📌-✅-1-trello-사용방법-리서치-및-발표">📌 ✅ 1. Trello 사용방법 리서치 및 발표</h3>
<h3 id="📌-✅-2-기능명세서-완성-후-팀원들과-공유하는-시간을-갖자">📌 ✅ 2. 기능명세서 완성 후 팀원들과 공유하는 시간을 갖자</h3>
<h3 id="📌-✅-3-기획--프론트--백엔드가-설계를-같이해본다">📌 ✅ 3. 기획 + 프론트 + 백엔드가 설계를 같이해본다</h3>
<h3 id="📌-4-더-많은-피드백을-얻기-위한-방안을-마련한다">📌 4. 더 많은 피드백을 얻기 위한 방안을 마련한다.</h3>
<h3 id="📌-5-피그마에-변경내역만-있는-명세-추가">📌 5. 피그마에 변경내역만 있는 명세 추가</h3>
<h3 id="📌-6-케이스-별로-화면-구성하기">📌 6. 케이스 별로 화면 구성하기</h3>
<h3 id="📌-7-피드백-받은-것을-문서화-및-리뷰">📌 7. 피드백 받은 것을 문서화 및 리뷰</h3>
<h3 id="📌-8-개발에서-마주한-문제를-팀에게-공유하자">📌 8. 개발에서 마주한 문제를 팀에게 공유하자</h3>
<h3 id="📌-9-preview-deploy-도입-검토">📌 9. Preview Deploy 도입 검토</h3>
<h3 id="📌-10-그라운드-룰-다시-작성-후-상기하는-시간">📌 10. 그라운드 룰 다시 작성 후 상기하는 시간</h3>
<h3 id="📌-11-성능-개선">📌 11. 성능 개선</h3>
</li>
</ul>
<h1 id="📌-회고의-장점">📌 회고의 장점</h1>
<h2 id="📌-간단하고-직관적">📌 간단하고 직관적</h2>
<ul>
<li>Keep, Problem, Try라는 3가지 요소로 구성되어 있어 누구나 쉽게 적용할 수 있었습니다.</li>
<li>팀원 간 피드백을 자연스럽게 유도하여 그동안의 답답한 부분을 해소할 수 있었습니다.<h2 id="📌-팀-성장">📌 팀 성장</h2>
</li>
<li>회고를 통해 문제점을 파악하고, 해결 방안을 함께 고민할 수 있다.</li>
<li>지속적인 개선이 가능</li>
<li>Try에서 나온 개선 사항을 실천하고, 이후 회고에서 점검하는 방식으로 지속적인 개선이 가능하다.</li>
<li>긍정적인 팀 문화 형성에 기여합니다.</li>
<li>단순히 문제를 지적하는 것이 아니라 잘된 점(Keep)을 공유하며 동기부여할 수 있다.<h1 id="📌-kpt회고가-필요했던-이유">📌 KPT회고가 필요했던 이유</h1>
<h2 id="📌-적극성-부여">📌 적극성 부여</h2>
팀의 성장 때문에 필요했다. 팀이 성장한다는 것은 팀원들 간의 긍정적인 상호작용이 일어나고 있다는 뜻이다. 우리 팀에서 가장 부족했던 것은 적극성이다.
찔러야 나오는 것이 아쉬웠다. 찌르기 전에, 문제가 있으면 이야기하고 소통할 수 있어야 했는데 그렇지 못했다. 그러다가 서로의 입장이 쌓인 뒤, 유야무야 푸시해서 진행된 적도 몇 차례 있었다. 그래서 회고가 어찌보면 늦은 감이 있었나? 싶을 정도로 필요 했다.<h2 id="📌-발언의-기회-부여">📌 발언의 기회 부여</h2>
각 팀원들에게는 일부는 쌓아두는게 어쩔 수 없는 선택이기도 하다. 왜냐하면 어떤 직책 혹은 책임을 지지 않고 동등한 레벨의 멤버에게 어필하기란 쉽지 않다. 게다가 전달하고자 하는 내용이 상처가 될 수도 있기 때문에 문제와 해결책을 알면서도 마음 속에 쥐고 있을 수 밖에 없다고 생각한다. 
KPT회고는 팀원들에게 의사를 말할 수 있는 기회를 제공하고, 다른 팀원들의 상황을 이해하게 만든다. 이후에는 종합적인 상황안에서 모두가 동의하는 해결방안을 마련할 기회를 제공한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[뽀또픽]기획에서의 문제정의와 가설 수립 및 검증하기 ]]></title>
            <link>https://velog.io/@oh_yunseong/%EB%BD%80%EB%98%90%ED%94%BD%EA%B8%B0%ED%9A%8D%EC%97%90%EC%84%9C%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%95%EC%9D%98%EC%99%80-%EA%B0%80%EC%84%A4-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@oh_yunseong/%EB%BD%80%EB%98%90%ED%94%BD%EA%B8%B0%ED%9A%8D%EC%97%90%EC%84%9C%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%95%EC%9D%98%EC%99%80-%EA%B0%80%EC%84%A4-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Sun, 16 Mar 2025 15:54:38 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>PM을 맡고 4주차... 큰 고민과 시련에 빠졌다.
참신한 아이디어로 시작한 프로덕트가 더 이상 참신해 보이지 않았고, 이게 사용자의 문제를 해결해줄 것 같지 않았다.
사실 팀원들은 큰 걱정이 없었을 수도 있다. 그러나 나는 알 수 없었다.
기존에 있던 서비스도 아니고, 이제 막 1차 MVP를 수립하고, 인프라를 구축하던 시점이니 의견을 구하고 싶다는 생각이 들었다.
개인적인 나의 성향상 과몰입이 심한 사람인데, 문득 &quot;사진을 SNS에 올리기 전, 고민되는 사진을 투표하는 어플리케이션&quot;이라는 주제가 정말 사용자들의 문제를 해결해 주는 어플리케이션인가? 라는 의문이 맴돌았다. 
내가 이프로젝트를 하면서 가장 진지하고 심도있게 빠졌던 시기 같다. </p>
</blockquote>
<h2 id="✔️-문제정의와-가설수립-검증">✔️ 문제정의와 가설수립, 검증</h2>
<p>그래서 나는 혼자 기획에서의 문제와 가설 그리고 검증이라는 것에 대해서 공부해보고, 우리 서비스에 대입하여 생각해보는 과정을 거쳤다.</p>
<p>문제란 무엇일까?
보통 문제는 현상으로 나타난다. 우리의 경우에는 SNS에 사진 올리기전에 어떤 사진을 올릴지 고민된다. 라는 현상을 발견했다. 근데 우리는 이 현상을 문제로 정의하는 오류를 범했다. 
현상은 문제가 아니다. 내 나름의 문제와 현상의 차이를 정의 헀다.</p>
<ol>
<li>해결할 가치가 있는가?</li>
<li>사용자들이 적극적을 해결책을 찾는가?</li>
<li>사용자들이 시간과 돈을 쓰는 문제인가?</li>
</ol>
<p>문제는 위와같은 물음에 Yes라는 답이 나와야 기획단에서 정의 하는 문제인 것이다. 
사용자는 생각보다 더 냉정하고 게으르기 때문에 조금의 불편함은 감수하고 산다. 그렇기 때문에 <strong>&#39;중요한 문제&#39;가 아니면 관성에 의해 살던 대로 살 가능성이 크다.</strong></p>
<p>그렇다면 우리는 우리가 발견한 이 현상을 어떻게 문제로 정의할 것이냐? 라는 큰 숙제에 직면한 것이다.</p>
<p>문제를 추론해가는 과정에서 추상적인 생각 지양, 비판적인 사고, 가치제공에 대한 의식 등이 필요하다. 나는 이런 상황에서 아무리 머리를 굴려봐도 뾰족한 수가 나오진 않았다. 그래서 나름대로의 문제정의와 가설과 검증방법에 대한 내용을 정리하여 팀원들과 의논하는 시간을 가졌다.</p>
<h2 id="✔️-팀원들과의-논의">✔️ 팀원들과의 논의</h2>
<p>팀원들은 어떻게 생각하고 있었을까?
팀원들의 생각</p>
<ul>
<li><p>우리는 사용자가 느끼고 있는 <strong>&#39;불편함&#39;</strong>을 찾지는 않았다.</p>
</li>
<li><p>우리는 사용자의 <strong>&#39;욕구</strong>&#39;를 충족시켜줄 현상을 찾았다.</p>
</li>
<li><p>그 현상은 <strong>SNS에 가장 잘 나온 사진</strong>을 올리고 싶었다는 것.</p>
</li>
<li><p>그럼 우리는 그 작은 현상이라도 투표를 통해 이 욕구를 충족시켜줄 문제를 정의하면 된다.</p>
<p>나는 이 이야기속에서 큰 깨달음을 얻었다. 어떻게 6주짜리 프로덕트에서 이 세상에 없어서 해결할 가치가 있으며, 사용자들이 적극적으로 해결책을 찾고, 시간과 돈까지 쓰는 문제를 우리가 정의할 수 있을까? 그건 불가능하다. 
그 덕에 다시 초심으로 돌아와 &#39;작은 문제라도 해결하자&#39; 라는 생각이 들었다. 이후, 사용자에게 피드백 받아 또 다시 문제를 세우고 가설을 검증하며 다시 사용자에게 피드백 받는 과정을 거치기로 마음 먹었다. 이 과정을 <strong>이터레이션</strong> 이라고 하는데 이 과정속에서 우리 서비스가 <strong>사용자의 문제를 해결해줄 수 있도록 구체화되고 확장</strong>될 것이라고 생각한다.</p>
</li>
</ul>
<h2 id="✔️-문제정의에-대한-결론">✔️ 문제정의에 대한 결론</h2>
<p>이후, 나는 우리 프로덕트의 문제를 다음과 같이 정의 했다.</p>
<h3 id="👿-결정-피로-문제">👿 결정 피로 문제</h3>
<blockquote>
<p>사용자들은 SNS에 사진을 올리기전에 선택의 어려움을 겪으며, 이과정에서 혼자만의 고민과 불필요한 에너지를 소비한다</p>
</blockquote>
<p>👉 <strong>투표를 통해 빠르게 결정을 내릴 수 있다.</strong></p>
<h3 id="👿-긍정적인-반응을-원하는-욕구">👿 긍정적인 반응을 원하는 욕구</h3>
<blockquote>
<p>사용자들은 SNS에 사진을 올리고 긍정적인 반응을 얻고자 하지만, 어떤 사진이 더 좋은 반응을 얻을지 예측하기 어렵다.</p>
</blockquote>
<p>👉 ** 어떤 사진이 인기있을지 투표를 통해 예측해볼 수 있다.** </p>
<p>이 두가지 문제는 사용자들의 행동과 심리를 미루어 짐작해, 해결할 만한 문제로 구체화 해본 것이다. 
그렇다면 이 문제에 대한 가설을 세우고 검증하는 과정을 거쳐 서비스의 핵심 줄기를 만들어야한다.
이전에는 정량적 리서치를 통해 위 문제들에 대한 해결 니즈를 확인한 상태였다. 이후에 또 다른 <strong><em>💡 검증 결과를 토대로 계속 이터레이션 과정을 거치며 사용자에게 선택받는 프로덕트로 발전시켜봐야 하는것이 숙제.</em></strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[사이드프로젝트]아이디에이션 과정(2) + 회고]]></title>
            <link>https://velog.io/@oh_yunseong/%EC%82%AC%EC%9D%B4%EB%93%9C%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%95%84%EC%9D%B4%EB%94%94%EC%97%90%EC%9D%B4%EC%85%98-%EA%B3%BC%EC%A0%952-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@oh_yunseong/%EC%82%AC%EC%9D%B4%EB%93%9C%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%95%84%EC%9D%B4%EB%94%94%EC%97%90%EC%9D%B4%EC%85%98-%EA%B3%BC%EC%A0%952-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Tue, 04 Mar 2025 12:40:37 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>굉장히 바쁜 한 주가 지나갔다.
갑자기 회사에서 발생한 큰 이슈 때문에 사이드프로젝트에 집중을 하지 못했다.
매일 참여해야한다는 생각 때문에 야근까지 하고 와서 사이드프로젝트를 해보려하니, 도저히 집중할 수 없었다.
차라리 쉬는게 나을 뻔했다.</p>
</blockquote>
<h2 id="pm으로서-아쉬웠던-점-😢">PM으로서 아쉬웠던 점 😢</h2>
<p>저번 주는 아이디에이션을 구체화하고 1차 MVP를 어떻게 설정하느냐에 초점이 맞춰졌다. 그러면서 WEB/WAS서버 환경 구축 및 개발 워크플로우 작업을 병행했다. </p>
<p>처음 PM을 해보기 때문에 이런 방향으로 나아가는게 맞나 싶다. <code>웹프로덕트 개발이론</code>이나 <code>협업 시스템</code>을 현재의 프로젝트에 적용해 보면서, 효용성을 체크하고 보완하고 싶은 욕심이 매우 컸다. 그래서 비슷한 서적과 강의, 블로그 등을 구비해놨는데 생각보다 프로젝트가 빨리 시작됐고, 회사 일도 있다보니 <strong><em>우선순위에서 계속 멀어졌다.</em></strong></p>
<p>그래서 아이디에이션 과정에서도 충분한 리서치나 아이디에이션 도구 활용 등에 대한 적극성은 좀 떨어졌다. 
그래도 <strong><em>구글폼을 활용해 리서치 해본 것</em></strong>은 프로덕트를 구상하는 관점에서 많은 도움이 됐다.(이게 유일한 리서치😢)</p>
<h2 id="pm으로서-배웠던-점-😃">PM으로서 배웠던 점 😃</h2>
<p>아마도 처음만난 팀원과도 어색하다보니, 의견공유도 적극적으로 이루어지진 못했다. 그래도 <strong><em>팀원 모두가 기획의 첫 단계에 참여</em></strong>한다는 것은 큰의미가 있다.</p>
<ol>
<li><strong><em>프로덕트에 오너쉽</em></strong>을 갖고 시작할 수 있다. <ul>
<li>자기가 하고 싶은 주제를 갖고 디자인과 개발을 시작한다는 것은 추후에 어려운일이 생겼을 때 끝까지 마무리 하고자 하는 데 큰 도움이 된다. </li>
</ul>
</li>
<li>프로덕트에 대한 관심은 <strong><em>개발의 완성도</em></strong>에 기여한다.<ul>
<li>프로덕트에 대한 이해, 비즈니스 로직에 대한 이해는 개발 완성도에 큰 영향을 미친다. 당연히 관심이 많을수록 Usecase, 예외처리 등 비즈니스에 대해 깊게 고민해 볼 수 있고, 팀원과 수 없이 많은 의견을 공유할 수 있다. </li>
</ul>
</li>
</ol>
<p>많은 시간을 아이디에이션과 첫 기획에 담아보면서 리서치도 부족하고, 적극적인 협업도 부족했다. 그래도 이런 어리숙함 속에서 모두가 원하는 주제를 선정했다는 것은 만족스러웠다.</p>
<p>앞으로 진행할 모든 일들에 대해 미숙함은 발생할 수 밖에 없고, 받아들이고 발전하는 것은 시간의 몫에 달렸다고 생각한다. 그래서 더 앞으로 있을 일들에 대한 <strong><em>배움과 기록</em></strong>이 중요할 것이다. </p>
<p><img src="https://velog.velcdn.com/images/oh_yunseong/post/28d53887-21ad-434e-a764-1437d684d73f/image.png" alt="뽀또픽"></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[사이드프로젝트]아이디에이션 과정(1) + 회고]]></title>
            <link>https://velog.io/@oh_yunseong/%EC%82%AC%EC%9D%B4%EB%93%9C%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%95%84%EC%9D%B4%EB%94%94%EC%97%90%EC%9D%B4%EC%85%98-%EA%B3%BC%EC%A0%951-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@oh_yunseong/%EC%82%AC%EC%9D%B4%EB%93%9C%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%95%84%EC%9D%B4%EB%94%94%EC%97%90%EC%9D%B4%EC%85%98-%EA%B3%BC%EC%A0%951-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Fri, 24 Jan 2025 15:55:18 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-아이디에이션">📌 아이디에이션</h1>
<h2 id="📌-중요성">📌 중요성</h2>
<ul>
<li>아이디에이션은 첫 시작이자, 가장 중요한 관문이라고 생각한다.</li>
<li>얼마나 만족스러운 아이디어가 나오느냐에 따라 성패가 달라질 것이라고 생각한다.</li>
<li>서비스가 대중들에게 선택받지 않더라도, 만드는 과정 내내 확신과 오너쉽으로 프로덕트 개발에 빠져들 수 있도록 해야한다.
또, 서비스가 전달하는 핵심이 직관적이고 명확하며, 쉽게 다가와야한다.<blockquote>
</blockquote>
<ul>
<li>와이어프레임, 기능명세, 화면설계, 요구사항, UserInterface 등의 문서작업과정이 빠르고 확실할 수 있다.</li>
<li>디자이너 및 개발과 제품기획에 대해 피드백 하는 과정이 수월할 것으로 예상된다.</li>
<li>개발단계에서 지치지 않고 지속적으로 나아갈 수 있는 동기를 부여한다.</li>
</ul>
</li>
</ul>
<h2 id="📌-아이디에이션을-도와주는-툴-블로그-이미지">📌 아이디에이션을 도와주는 툴, 블로그, 이미지</h2>
<ul>
<li>피그잼 - 잼봇</li>
<li>미로 - 미로어시스트</li>
<li><a href="https://brunch.co.kr/@msmmx/4">https://brunch.co.kr/@msmmx/4</a></li>
<li>비즈니스모델 캔버스
<img src="https://velog.velcdn.com/images/oh_yunseong/post/56170fc3-8e79-4982-a845-cfc8f7fd52f8/image.png" alt="비즈니스모델캔버스"></li>
</ul>
<h2 id="📌-회고">📌 회고</h2>
<ul>
<li>아이디에이션 과정이 상당히 힘들었다. <ul>
<li>문제를 발견, 인식하는 것이 사람마다 달랐다.</li>
<li>무자본, 콜드스타트 상태에서 서비스의 핵심가치를 정의하는 것이 어렵다.</li>
<li>핵심가치가 나오더라도 어떻게 서비스로 녹일지, 방법론 등에 대한 지식과 정의가 부족하다.</li>
</ul>
</li>
<li>내일 15시에 디스코드로 다시 만나서 아이디에이션을 이어나가기로 했다. 그 전까지 아이디어를 디벨롭 하는 과정에서 어떤 부분을 중점적으로 두고 서비스로 발전시킬지 항목화 해야겠다.</li>
<li>항목화한 리스트에 아이디어에 대한 질의를 쌓아가면서 디벨롭 하고, 육각형의 아이디어가 나올 수 있도록 하자.</li>
<li>기획단계에서 비용구조, 수익원, 파트너쉽 등 금전과 관련된 부분에 대해 논의를 하는 것은 거의 삭제되는 수준이었다. 상업적 목적의 어플리케이션 개발이 아니라는 것을 감안하여 위와 같은 부분은 소거하여 진행해야하는 것인지 고민이 된다.😂</li>
<li>팀원분들이 정말 많이 도와줬다. 사이드프로젝트 유경험자, 현업경험 있으신 분들이 기획의 가이드라인을 주셨다. 
기획은 생각보다 많은 역량을 요구했는데, 부족한 점이 많았다. 그냥 백앤드개발로 지원할걸... 이라는 생각이 1주동안 엄청 들었다ㅋㅋ... 하던게 이것 뿐이라 기획을 하면서도 어떤 기술스택을 쓸까, 서버구성 및 아키텍처는 어떤게 좋을까 등 기술쪽으로 자꾸 생각이 치우쳤다. PM의 역할을 수행하는데 있어서 오히려 이런 것들이 방해가 되기도 했다.</li>
<li>도움받은 부분<ul>
<li>기획자가 어떤 방식으로 각 분야의 담당자들과 워크플로우를 구성해야하는지(커뮤니케이션 방법)</li>
<li>기획단계에서 사용자 요구사항을 받는 방법(인터뷰, 리서치 등)</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[PM사이드프로젝트] - 팀빌딩 전, 동기와 목표설정]]></title>
            <link>https://velog.io/@oh_yunseong/%EC%82%AC%EC%9D%B4%EB%93%9C%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%8C%80%EB%B9%8C%EB%94%A9-%EC%A0%84-%EB%8F%99%EA%B8%B0%EC%99%80-%EB%AA%A9%ED%91%9C%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@oh_yunseong/%EC%82%AC%EC%9D%B4%EB%93%9C%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%8C%80%EB%B9%8C%EB%94%A9-%EC%A0%84-%EB%8F%99%EA%B8%B0%EC%99%80-%EB%AA%A9%ED%91%9C%EC%84%A4%EC%A0%95</guid>
            <pubDate>Wed, 15 Jan 2025 05:22:14 GMT</pubDate>
            <description><![CDATA[<h1 id="📌사이드프로젝트를-하는-이유">📌사이드프로젝트를 하는 이유</h1>
<h2 id="📌직무-간접경험---pm">📌직무 간접경험 - PM</h2>
<blockquote>
</blockquote>
<p>현재는 개발자든 설계자든 PM이든 어떤 IT직무에도 기술만 맞으면 지원할 수 있다는 생각을 한다.
근데 내가 개발, 설계, 기획을 모두 경험해보면서 서비스기획과 현실적인 아이디어를 제안하는 일련의 과정이 가장 흥미 있었다.
폭넓은 IT지식을 갖추고 싶고, 프로덕트가 개발부터 릴리즈까지 어떤 요소가 필요하고 어떤 과정을 거쳐야하는지 알고 싶다.
개발을 하다보면, 서비스를 구성하는 기술에 깊은 전문가가 된다. 근데 나는 이런 저런 기술에 관심이 많고, 그 기술로 이루어진 혁신적인 서비스에 관심이 많다.
그래서 사이드 프로젝트를 하면서 PM의 업무를 간접경험 해보고 싶다.</p>
<h2 id="📌네트워킹">📌네트워킹</h2>
<blockquote>
</blockquote>
<p>비전공자인 내가 백앤드, 프론트엔드, 디자이너 등의 직무를 꿈꾸는 사람들을 만날 수 있는 기회가 있다.
이들이 각자 갖고 있는 경험이 있을 것이고, 나와 공감할 수 있는 경험, 내가 겪어보지 못한 경험들을 나눌 수 있을 것이라 생각한다.
서로의 경험을 공유하고 같은 프로덕트를 만들다 보면 든든한 동료, 친구가 생길 것 같아 기대된다.</p>
<h2 id="📌장기적-지속">📌장기적 지속</h2>
<blockquote>
</blockquote>
<p>사이드프로젝트는 기술과 역량을 키우기 위한 것이다. 경력사항에 큰 자리를 차지하진 않는다.
나도 마찬가지로 IT업계종사자로서 회사말고 IT관련 재미있는 무엇인가를 만들어가고 싶어서 사이드프로젝트에 관심을 가졌다.
회사와 집을 반복하는 직장인1 보다는 경력 외로 다른 IT세상을 맛보고 장기적으로 이 일에 흥미를 두고 싶다.</p>
<h1 id="📌사이드프로젝트를-어떻게-시작할-수-있을까">📌사이드프로젝트를 어떻게 시작할 수 있을까</h1>
<h2 id="📌it동아리">📌IT동아리</h2>
<ul>
<li>대학생이 아니기 떄문에 PASS</li>
<li>강제성과 기한의 부재로 인해 늘어질 가능성이 있다.<h2 id="📌동료-친구">📌동료, 친구</h2>
</li>
<li>가장 어려운 것 같다. </li>
<li>프로젝트라 하면 트러블의 여지도 있기 때문에 이런부분을 해결하는게 동료나 친구는 어렵다.</li>
<li>이탈의 확률이 크다.<h2 id="📌모집-사이트">📌모집 사이트</h2>
</li>
<li>여러 사이트가 있다. 렛플이라든지 수프라든지 OKKY 등...</li>
<li>여기서 한군데를 정해서 지원하는 것. 나쁘지 않다. </li>
<li>대신 나의 목적과 사이드프로젝트의 목적, 조직문화 등이 일치하는지 잘 확인해봐야 한다.<h2 id="📌해커톤">📌해커톤</h2>
</li>
<li>많은 시간이 들어가서 일을 하면서 사이드로 하기에는 어렵다고 생각했다.</li>
<li>다양한 해커톤들이 진행되는데, 이 기회가 항상 있는것이 아니라 진행될때를 노려야한다.</li>
<li>만약 하게 되면 짧은 시간에 각 분야별로 모여서 할 수 있어서 좋다.</li>
</ul>
<h1 id="📌사이드프로젝트로-얻고자-하는-것">📌사이드프로젝트로 얻고자 하는 것</h1>
<h2 id="📌자신감-네트워킹-기술연습-이력보완">📌자신감, 네트워킹, 기술연습, 이력보완</h2>
<blockquote>
<ul>
<li>PM으로 직무전환을 고려하면서 작지만, 완성된 어플리케이션을 만들어보고 싶다. 
이 경험을 통해 내가 <strong>직무에 적합한 사람</strong>인지, 적합하다면 이 경험을 토대로 <strong>관련 기술</strong>을 익히고, 자신감도 가질 수 있을것 같다.
네트워킹은 덤!</li>
</ul>
</blockquote>
<p><img src="https://velog.velcdn.com/images/oh_yunseong/post/45e8cffa-82ed-46d0-8aea-1f1e92e269c0/image.jpg" alt=""></p>
<blockquote>
<ul>
<li>현업에서 일하면서 배운 지식과 경험을 작은 프로젝트 안에 녹여보고 싶다.</li>
</ul>
</blockquote>
<h2 id="📌실패경험을-복기해서-완주로-마무리하기">📌실패경험을 복기해서 완주로 마무리하기</h2>
<blockquote>
<p>사이드 프로젝트는 보수가 없다. 말그대로 하고 싶어 하는것.
끝까지 완주하기 위해 실패경험을 적어보고, 보완점을 토대로 프로젝트 완주해보자.</p>
</blockquote>
<h2 id="📌실패경험-😠팀내불화">📌실패경험. 😠팀내불화</h2>
<p><img src="https://velog.velcdn.com/images/oh_yunseong/post/1db91973-759c-4825-9bda-01bb50d1ceaa/image.png" alt="불화"></p>
<h3 id="📌원인">📌원인</h3>
<ul>
<li><strong>기대가 일치하지 않음</strong><ul>
<li>각자 프로젝트에서 기대가 있고, 목적, 목표 등이 있을 것이다.
누구는 커뮤니티를 원하고, 누구는 기술적 발전에 초점을 둘 수도 있다. 
그런 팀원들과 의사소통 없이 <u>마감에만 집중하고 어떠한 그라운드 룰 없이 프로젝트가 진행됐다.</u> 
당연히 서로 다른 목표 때문에 속안의 스트레스, 불만이 쌓일 수 밖에 없었다.</li>
</ul>
</li>
<li><strong>솔직함의 부재</strong><ul>
<li>솔직하지 못해 이런 불만들이 쌓이게 두었다. <u>솔직하게 불만을 표출하면 친절함을 잃고 싸움이 될까 두려웠기 때문이다.</u>
결국, 마감 때 터지고 말았다.<h3 id="📌해결방안">📌해결방안</h3>
지금와서 생각하면, 꼭 말해야했다. 그리고 말하는 방식에 대한 우리만의 <strong>문화가 필요했다.</strong> 서로 합의된 룰 안에서 솔직하다면, <u>솔직함은 친절함과 배타적인 의미가 아닐 것이다.</u></li>
</ul>
</li>
</ul>
<blockquote>
</blockquote>
<ol>
<li>컴플레인 무조건 말하기<ul>
<li>어떤 내용이든 속안의 내용들은 무조건 말해야 한다.</li>
<li>컴플레인 표출에 대한 긍정적 함의 필요.</li>
</ul>
</li>
<li>말하는 방식 정하기<ul>
<li>~님 사용, 존댓말 유지</li>
</ul>
</li>
<li>각자 목적, 목표, 동기, 원하는 바 등을 공유하고 인지.(<code>팀캔버스</code>활용)</li>
<li>안점감과 소속감, 주인의식을 갖기 위한 각 인원 존중.</li>
<li>완성된 어플리케이션에 대한 기대치 맞추기(개발 및 설계의 범위, 기술수준 등)</li>
<li>결정의 방법과 주체를 정하자.(<code>기술PL</code>필요)</li>
<li>숙제가 아니라 활동한다고 생각하기.</li>
<li>일이 아닌 기여라고 생각하기.</li>
<li>마감 집중에서 우선순위와 속도에 집중하기(<code>우선순위매트릭스</code> 활용)</li>
</ol>
<h1 id="📌사이드프로젝트를-완주하기-위해-필요한-요소는-뭘까">📌사이드프로젝트를 완주하기 위해 필요한 요소는 뭘까</h1>
<p>사이드프로젝트는 <strong>경제성도 보수도 경력</strong>도 아니다.😂
무엇이 Grit(힘들어도 이겨낼 수 있는 능력!)을 가지며 꾸준하게 프로젝트를 진행할 수 있을까?</p>
<ol>
<li><strong>성취와 성장</strong></li>
<li><strong>경험과 탐험</strong></li>
<li><strong>관계</strong></li>
</ol>
<p>이러한 요소들이 사이드프로젝트를 완수할 수 있는 주 요소가 될 것이다. 나 같은 경우는 2번 경험과 탐험이다. </p>
<blockquote>
<ol>
<li>팀원들과 팀빌딩 시간에 자유롭게 이야기해 보고 싶다.
각자 프로젝트 <strong>성공의 정의</strong>는 무엇인지, 각자 <strong>능력의 한계</strong>는 무엇인지, 하고 싶은 <strong>과제와 역할</strong>은 무엇인지...등등
프로젝트는 <strong>함께 풀어야할 문제</strong>이다. 역할을 부여받고 <strong><del>혼자 풀어내야할 문제</del></strong>가 아니라는 점을 분명히 하고 싶다.
<img src="https://lutonmuslimjournal.com/wp-content/uploads/2021/02/gettyimages-803227220.jpg" alt="Grit"></li>
</ol>
</blockquote>
<blockquote>
<ol start="2">
<li>편안한 분위기에서 프로젝트 회고는 필수다. 각자 매주 간단한 회식이든 식사든 만남을 갖고 회고하는 시간을 가져보고 싶다. <strong>의미는 만드는 것이 아니라 찾는 것이라 생각한다.</strong> 
<img src="https://t1.daumcdn.net/cfile/tistory/2233AD4F5502417F07" alt=""></li>
</ol>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[쿼리 튜닝으로 데이터베이스 성능을 개선하는 방법]]></title>
            <link>https://velog.io/@oh_yunseong/%EC%BF%BC%EB%A6%AC-%ED%8A%9C%EB%8B%9D%EC%9C%BC%EB%A1%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%84%B1%EB%8A%A5%EC%9D%84-%EA%B0%9C%EC%84%A0%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@oh_yunseong/%EC%BF%BC%EB%A6%AC-%ED%8A%9C%EB%8B%9D%EC%9C%BC%EB%A1%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%84%B1%EB%8A%A5%EC%9D%84-%EA%B0%9C%EC%84%A0%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Tue, 14 Jan 2025 08:51:43 GMT</pubDate>
            <description><![CDATA[<h1 id="데이터베이스-성능-최적화를-위한-쿼리-튜닝-방법">데이터베이스 성능 최적화를 위한 쿼리 튜닝 방법</h1>
<p>ERP를 설계하다보면, 수많은 테이블의 조인연산이 이뤄지고, 원하는 데이터를 얻기위해 서브쿼리도 자주 사용하였습니다. 이렇게 되다 보니, 조인을 10~20개씩 걸어서 데이터를 조회 했습니다. 
예를들어, 프로젝트 관련된 정보를 조회하고 싶다. </p>
<blockquote>
<p>프로젝트 테이블 - 영업기회 테이블 - 수주 테이블 - 사원 테이블 - 부서 테이블 - 거래처 테이블 - 신용정보 테이블 - 프로젝트 타입 테이블 - 라이선스 테이블... </p>
</blockquote>
<p>원하는 데이터를 얻고자 쿼리는 계속 늘어나게 됐습니다.
처음에는 성능상의 이슈가 없다가도 데이터가 늘어나게 되면 WAS서버가 다운되는 일까지 발생했습니다.</p>
<p><img src="https://cdn-icons-png.flaticon.com/512/10772/10772726.png" alt="서버다운">  </p>
<p>이를 해결하기 위해, 다양한 방법으로 쿼리를 튜닝해보고 비즈니스에 맞게 DB 아키텍쳐도 구성해보는 등 의미 있는 시간을 가져봤습니다.</p>
<hr>
<h2 id="1-인덱스index-설계-최적화">1. 인덱스(Index) 설계 최적화</h2>
<p>인덱스는 데이터 검색 속도를 크게 향상시키는 데이터베이스 구조입니다. 하지만 모든 컬럼에 인덱스를 추가하는 것은 성능 저하를 초래할 수 있습니다. </p>
<h3 id="기술적-원리와-작동-방식">기술적 원리와 작동 방식</h3>
<ul>
<li><strong>B-Tree 기반 인덱스</strong>: 데이터를 정렬된 트리 구조로 저장하여 검색 연산의 시간 복잡도를 O(log n)으로 감소.</li>
<li><strong>해시 기반 인덱스</strong>: 키 값에 대한 해시 함수를 사용하여 데이터 위치를 직접 참조, 특정 값을 찾는 데 매우 빠름.</li>
<li><strong>클러스터드 인덱스</strong>: 테이블의 실제 데이터가 인덱스 순서에 따라 정렬.</li>
</ul>
<h3 id="적용-예시">적용 예시</h3>
<ul>
<li>조회 빈도가 높은 <code>user_id</code> 컬럼에 인덱스를 추가.</li>
<li>주문 상태(<code>status</code>)를 기준으로 조회하는 경우 <code>status</code> 컬럼에 인덱스를 설정.</li>
</ul>
<pre><code class="language-sql">CREATE INDEX idx_user_id ON users(user_id);
CREATE INDEX idx_status ON orders(status);</code></pre>
<p><img src="https://velog.velcdn.com/images/rungoat/post/f188004e-e3dc-4d4d-a3cc-cf69cc699f23/image.png" alt="인덱스 구조"><br><em>예시: B-Tree 인덱스 구조</em></p>
<hr>
<h2 id="2-쿼리-작성-방식-개선">2. 쿼리 작성 방식 개선</h2>
<p>비효율적인 쿼리는 데이터베이스 리소스를 과도하게 소모합니다. 이를 방지하기 위해 쿼리를 간소화하고 최적화해야 합니다.</p>
<h3 id="기술적-원리와-작동-방식-1">기술적 원리와 작동 방식</h3>
<ul>
<li><strong>SELECT 최적화</strong>: <code>SELECT *</code>는 모든 컬럼을 불필요하게 조회하므로 특정 컬럼만 선택.</li>
<li><strong>JOIN 최적화</strong>: 서브쿼리는 각 쿼리를 독립적으로 실행하므로, JOIN으로 병합하여 성능 개선.</li>
<li><strong>필터 조건 최적화</strong>: WHERE 절을 사용하여 데이터를 미리 필터링.</li>
</ul>
<h3 id="적용-예시-1">적용 예시</h3>
<ul>
<li><code>order_items</code> 테이블에서 수량(<code>quantity</code>)이 10 이상인 데이터를 조회.</li>
</ul>
<pre><code class="language-sql">-- 비효율적인 쿼리
SELECT * FROM orders WHERE id IN (SELECT order_id FROM order_items WHERE quantity &gt; 10);

-- 최적화된 쿼리
SELECT o.id, o.date
FROM orders o
JOIN order_items oi ON o.id = oi.order_id
WHERE oi.quantity &gt; 10;</code></pre>
<hr>
<h2 id="3-데이터-정규화와-비정규화">3. 데이터 정규화와 비정규화</h2>
<p>데이터 정규화는 중복을 최소화하고 데이터 일관성을 유지하는 데 효과적입니다. 반면, 비정규화는 읽기 성능을 높이는 데 유리합니다.</p>
<h3 id="기술적-원리와-작동-방식-2">기술적 원리와 작동 방식</h3>
<ul>
<li><strong>정규화</strong>: 테이블을 분리하여 중복 데이터를 제거하고 데이터 무결성 유지. 대표적으로 1NF, 2NF, 3NF 등이 있음.</li>
<li><strong>비정규화</strong>: 읽기 성능을 위해 필요한 데이터를 하나의 테이블에 결합하여 저장.</li>
</ul>
<h3 id="적용-예시-2">적용 예시</h3>
<ul>
<li><strong>정규화</strong>: 고객 테이블과 주문 테이블을 분리.</li>
<li><strong>비정규화</strong>: 고객 이름과 주문 데이터를 하나의 테이블에 저장.</li>
</ul>
<pre><code class="language-sql">-- 정규화된 테이블 설계
CREATE TABLE customers (
    customer_id INT PRIMARY KEY,
    name VARCHAR(100)
);

CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_id INT,
    date DATE,
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);

-- 비정규화된 테이블 설계
CREATE TABLE orders_with_customer (
    order_id INT PRIMARY KEY,
    customer_name VARCHAR(100),
    date DATE
);</code></pre>
<p><img src="https://blog.kakaocdn.net/dn/bJUNWP/btqE2twgz5B/Z9qC1T74uCQ1CyAbKnUG60/img.png" alt="정규화와 비정규화"><br><em>예시: 정규화와 비정규화 비교</em></p>
<hr>
<h2 id="4-실행-계획execution-plan-분석">4. 실행 계획(Execution Plan) 분석</h2>
<p>데이터베이스가 쿼리를 처리하는 과정을 분석하여 병목 지점을 식별할 수 있습니다.</p>
<h3 id="기술적-원리와-작동-방식-3">기술적 원리와 작동 방식</h3>
<ul>
<li><strong>Access Path</strong>: 테이블 전체를 스캔하는 Full Table Scan을 피하고 인덱스를 활용.</li>
<li><strong>Cost</strong>: 실행 계획에서 비용이 높은 연산(예: Sort, Hash Join 등)을 줄임.</li>
</ul>
<h3 id="적용-예시-3">적용 예시</h3>
<ul>
<li>EXPLAIN 명령어로 실행 계획 확인.</li>
</ul>
<pre><code class="language-sql">EXPLAIN SELECT o.id, o.date FROM orders o WHERE o.status = &#39;completed&#39;;</code></pre>
<hr>
<h2 id="5-데이터베이스-통계-정보-최신화">5. 데이터베이스 통계 정보 최신화</h2>
<p>최신 통계 정보는 쿼리 최적화 도구가 효율적인 실행 계획을 수립하는 데 도움을 줍니다.</p>
<h3 id="기술적-원리와-작동-방식-4">기술적 원리와 작동 방식</h3>
<ul>
<li><strong>옵티마이저(Optimizer)</strong>: 통계 정보를 활용하여 가장 적합한 실행 계획을 자동으로 선택.</li>
<li><strong>통계 정보 갱신</strong>: 테이블과 인덱스의 데이터 분포를 주기적으로 분석.</li>
</ul>
<h3 id="적용-예시-4">적용 예시</h3>
<ul>
<li><code>ANALYZE</code> 명령어로 통계 정보 갱신.</li>
</ul>
<pre><code class="language-sql">ANALYZE TABLE orders;</code></pre>
<p><img src="https://thebook.io/img/006977/227.jpg" alt="옵티마이저 작동 원리"><br><em>예시: 옵티마이저와 통계 정보</em></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[설계-디자인패턴]팩토리패턴을 사용해야하는 이유와 예시]]></title>
            <link>https://velog.io/@oh_yunseong/%EC%84%A4%EA%B3%84-%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4%ED%8C%A9%ED%86%A0%EB%A6%AC%ED%8C%A8%ED%84%B4%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0%EC%99%80-%EC%98%88%EC%8B%9C</link>
            <guid>https://velog.io/@oh_yunseong/%EC%84%A4%EA%B3%84-%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4%ED%8C%A9%ED%86%A0%EB%A6%AC%ED%8C%A8%ED%84%B4%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0%EC%99%80-%EC%98%88%EC%8B%9C</guid>
            <pubDate>Wed, 08 Jan 2025 08:33:46 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-개요">📌 개요</h1>
<ul>
<li><p>회사에서 소프트웨어 회사에 특화된 영업관리 모듈을 설계하고 개발하는 업무를 맡았다. 
<code>N개</code>의 고객사의 비즈니스에 맞게 ERP시스템을 설계하고 개발해야했다.
표준이 되는 패키지 서비스를 갖고 모든 업체의 비즈니스를 소화해야하기 때문에 무지성으로 만들면, 업체 전용으로 프로젝트가 과생산 되는 경우가 발생했다. </p>
</li>
<li><p>이렇게 되면, 향후 업체가 늘어날수록 <code>유지보수가 힘들어지고</code>, <code>추가공수</code>가 발생하는 문제가 생긴다.</p>
</li>
<li><p>예를 들면, <code>A업체</code>는 전자계약으로 <code>글로사인</code> 사용하고, <code>B업체</code>는 <code>스마트빌</code>을 사용할 수 있다. 
각 전자계약 업체마다 API가 다르니 업체 전용프로젝트를 만들어서 전용으로 나가는 방식을 채택하게 되면 향후에는 OO전자계약을 사용하는 </p>
</li>
<li><p>*<U>회사 N개에 N개의 무지성 클론프로젝트가 생기는 것**</U>이다.
<img src="https://velog.velcdn.com/images/oh_yunseong/post/64f518bb-6708-413e-a24c-3f89e1ba5b98/image.webp" alt=""></p>
</li>
<li><p>이를 <code>효율적으로 확장</code>하기 위해 여러가지로 찾아보다가 <code>팩토리패턴</code>으로 패키지서비스를 설계했다. </p>
</li>
</ul>
<hr>
<h1 id="📌-팩토리패턴">📌 팩토리패턴</h1>
<h2 id="✔️-장점">✔️ 장점</h2>
<ol>
<li><strong>객체 생성 코드 재사용 가능</strong><ul>
<li>객체 생성 로직을 캡슐화하여 코드 중복을 줄임.</li>
</ul>
</li>
<li><U><strong>유연성 증가</strong></U><ul>
<li>클라이언트 코드가 생성 로직에서 분리되어 새로운 타입의 객체를 쉽게 추가할 수 있음.</li>
</ul>
</li>
<li><U><strong>유지보수성 향상</strong></U><ul>
<li>객체 생성 로직이 한곳에 집중되어 변경이 쉬움.</li>
</ul>
</li>
<li><U><strong>결합도 감소</strong></U><ul>
<li>클라이언트 코드와 객체 생성 로직 간의 의존성을 줄임.</li>
</ul>
</li>
</ol>
<ul>
<li>나의 경우 <strong>여러 전자계약 시스템별로 유지보수 가능</strong>하면서, <strong>유연하게 확장</strong>해야 했다.</li>
<li>일단, 스프링을 활용한 팩토리패턴은 DI기능을 사용하여 객체생성을 프레임워크가 관리한다. <strong>등록기반팩토리패턴 활용</strong>에 좋다. </li>
<li>이유는 스프링 컨텍스트에서 팩토리 빈 생성 시점에 내가 정의 해놓은 전자계약 클래스들(Bean)을 주입받기 때문이다.</li>
</ul>
<hr>
<h2 id="✔️-전자계약의-종류에-따른-팩토리패턴-설계구조">✔️ 전자계약의 종류에 따른 팩토리패턴 설계구조</h2>
<h3 id="✔️-팩토리-패턴의-구성-요소">✔️ 팩토리 패턴의 구성 요소</h3>
<ul>
<li><code>Product</code>: 생성할 객체의 인터페이스 또는 상위 클래스.</li>
<li><code>ConcreteProduct</code>: 실제로 생성되는 객체.</li>
<li><code>Factory</code>: 객체를 생성하는 클래스 또는 메서드.</li>
</ul>
<hr>
<h3 id="✔️-설계코드">✔️ 설계코드</h3>
<ul>
<li><p>다양한 전자계약 방식(글로사인, 스마트빌 등)을 지원하는 애플리케이션에서, 계약방식에 따라 다른 객체를 생성해야 한다고 가정합니다.</p>
<pre><code class="language-java">/* 각 전자계약의 Product */
public interface eContract {
  void makeContract(contractModel contract); //전자계약에 필요한 변수들을 총망라한 모델 - ERP데이터를 기반으로 만들어진다.
}
/*******************************************************************/
/* 각 전자계약의 ConcreteProduct 구현체 */
@Component
public class Glosign implements eContract { //글로사인
  @Override
  public void makeContract(contractModel contract) {
      //글로사인 api호출 로직
  }
}
@Component
public class Smartbill implements eContract { //스마트빌
  @Override
  public void makeContract(contractModel contract) {
      //스마트빌 api 호출 로직
  }
}
/*******************************************************************/
/* 각 전자계약의 Factory Bean*/
@Component
public class ContractFactory {
  private final Map&lt;String, eContract&gt; contractMap = new HashMap&lt;&gt;();

  //생성자주입 - 빌드시 스프링컨텍스트에 위의 구현체를 바탕으로 자동으로 주입
  //팩토리에 구현해놓은 전자계약 클래스들을 등록하는 과정(등록기반팩토리)
  public ContractFactory(List&lt;eContract&gt; eContracts) {
      for (eContract econtract : eContracts) {
          contractMap.put(econtract.getClass().getSimpleName().toLowerCase(), econtract);
      }
  }

  public eContract getEContract(String type) {
      return contractMap.get(type.toLowerCase());
  }
}
/*******************************************************************/
// 클라이언트 코드
@RestController
public class ContractController {
  private final ContractFactory contractFactory;

  public ContractController(ContractFactory contractFactory) {
      this.contractFactory = contractFactory;
  }

  @GetMapping(&quot;/contract&quot;)
  public void contract(@RequestParam String type, @RequestParam contractModel contract) {
      eContract econtract = contractFactory.getEContract(type); 
      if (payment != null) {
          econtract.makeContract(amount); //구현해놓은 전자계약 클래스에 따라 다른 api작동
      } else {
          throw new IllegalArgumentException(&quot;Unknown payment type: &quot; + type);
      }
  }
}
/*******************************************************************/</code></pre>
</li>
</ul>
<hr>
<h3 id="✔️-디자인패턴-적용의-효과">✔️ 디자인패턴 적용의 효과</h3>
<ul>
<li>간단하게 등록방식의 팩토리패턴 설계 뼈대를 만든 것을 바탕으로 각 회사 시스템에 맞게 구현하였다. </li>
<li>실제로 다른 전자계약 시스템을 사용한다고 하면 나는 그저, 구현체 클래스를 하나더 만들어주면 끝일 뿐이다.(혹은 interface 수정) 나머지는 팩토리패턴이 알아서 맞춰서 호출해준다. </li>
<li>정처기때 배웠던 <code>개방폐쇄원칙(OCP)</code>에도 좋다고 한다. 확장에는 열려있고, 수정에는 닫혀있다는 의미다.
여러 고객사를 상대하는 패키지 서비스를 설계할 때는 이런 부분을 많이 알아두는게 좋을 것 같다.</li>
</ul>
<hr>
<h3 id="✔️-추가고려사항">✔️ 추가고려사항</h3>
<ul>
<li>그리고 현재 아쉬운점은 다양한 전자계약시스템에 대한 디자인패턴 적용은 완료하였으나, 
하나의 전자계약이라도 회사마다 사용방식이 다르기 때문에 이는 어떻게 효율적으로 관리할 것이냐를 또 생각해봐야겠다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java]Pattern(정규표현식)과 Matcher로 사용자 지정 포맷팅 구현하기]]></title>
            <link>https://velog.io/@oh_yunseong/Java-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%B2%98%EB%A6%AC-Pattern%EA%B3%BC-Matcher%EB%A1%9C-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%A7%80%EC%A0%95-%ED%8F%AC%EB%A7%B7%ED%8C%85-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D-%EA%B0%80%EB%B3%80%EC%9D%B8%EC%88%98-%ED%99%9C%EC%9A%A9</link>
            <guid>https://velog.io/@oh_yunseong/Java-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%B2%98%EB%A6%AC-Pattern%EA%B3%BC-Matcher%EB%A1%9C-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%A7%80%EC%A0%95-%ED%8F%AC%EB%A7%B7%ED%8C%85-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D-%EA%B0%80%EB%B3%80%EC%9D%B8%EC%88%98-%ED%99%9C%EC%9A%A9</guid>
            <pubDate>Thu, 12 Sep 2024 01:33:46 GMT</pubDate>
            <description><![CDATA[<h2 id="시작하기">시작하기</h2>
<p>웹 개발을 하다보면, 서버에서 문자열을 가공하여 넘겨줘야하는 상황들이 발생한다. 
예를 들면, 오늘 날짜를 <code>simpleDateFormat</code>을 활용하여 <code>yyyy/MM/dd</code>로 넘겨주거나 <code>DecimalFormat</code>을 이용하여 금액에 천단위 콤마를 찍어주는 일 등이 있다. </p>
<p>이를 사용자가 지정한 문자열 포맷으로 간편하게 변환해주는 공통메서드를 개발했다. 
기본적으로 <code>String.format()</code>과 유사한 기능을 제공하지만, 다양한 데이터 유형에 대해 사용자 지정 형식 문자열을 지원하여 더 유연하게 동작한다.</p>
<p>또한, 어떤문자열이든 <strong>원하는 대로 형식을 지정하여 변환된 문자열을 리턴</strong>받을 수 있기에 개발생산성에도 좋은 영향을 줄 것이다. </p>
<h2 id="코드">코드</h2>
<pre><code class="language-java">public static String Format(String format, Object... params) {
    if (StringUtil.isEmpty(format)) {
        return &quot;&quot;;
    }

    // 정규 표현식을 사용하여 {index:format} 형태의 패턴을 찾기
    Pattern pattern = Pattern.compile(&quot;\\{(\\d+)(:[^\\}]+)?\\}&quot;);
    Matcher matcher = pattern.matcher(format);

    // 인덱스에 따른 값과 기본 포맷을 저장할 맵 초기화
    Map&lt;Integer, Object&gt; paramValueMap = new HashMap&lt;&gt;();
    Map&lt;Integer, java.text.Format&gt; paramFormatMap = new HashMap&lt;&gt;();

    // 파라미터 값과 기본 포맷 지정
    for (int i = 0; i &lt; params.length; i++) {
        Object obj = params[i];
        paramValueMap.put(i, obj);
        paramFormatMap.put(i, obj != null ? getFormatter(obj, null) : null);
    }

    // 패턴을 찾으면서 형식화된 문자열로 대체
    StringBuilder result = new StringBuilder(format);
    while (matcher.find()) {
        String patternString = matcher.group();
        int idx = Convert.toInteger(matcher.group(1));
        String idxFormat = matcher.group(2);

        Object val = paramValueMap.get(idx);
        java.text.Format formatter = (idxFormat != null &amp;&amp; StringUtil.isNotEmpty(idxFormat.substring(1)))
                ? getFormatter(val, idxFormat.substring(1))
                : paramFormatMap.get(idx);

        // 대체할 값 설정 및 형식에 맞게 대체
        Object replaceValue = (val == null ? &quot;null&quot; : val);
        String replacement = (replaceValue != null &amp;&amp; formatter != null) 
                             ? formatter.format(replaceValue) 
                             : Convert.toString(replaceValue);

        // 패턴을 형식화된 값으로 대체
        int start = result.indexOf(patternString);
        if (start != -1) {
            result.replace(start, start + patternString.length(), replacement);
        }
    }

    return result.toString();
}

private static java.text.Format getFormatter(Object value, String formatExp) {
    if (value == null) return null;
    String className = value.getClass().getName();

    switch (className) {
        case &quot;java.util.Date&quot;:
            return StringUtil.isNotEmpty(formatExp) ? new java.text.SimpleDateFormat(formatExp) : new java.text.SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss&quot;);
        case &quot;java.lang.Integer&quot;:
            return StringUtil.isNotEmpty(formatExp) ? new DecimalFormat(formatExp) : new DecimalFormat(&quot;###,##0&quot;);
        case &quot;java.lang.Double&quot;:
        case &quot;java.lang.Float&quot;:
        case &quot;java.lang.Long&quot;:
        case &quot;java.math.BigDecimal&quot;:
            return StringUtil.isNotEmpty(formatExp) ? new DecimalFormat(formatExp) : new DecimalFormat(&quot;###,##0.##&quot;);
        default:
            return null;
    }
}</code></pre>
<h2 id="사용예시">사용예시</h2>
<ol>
<li>기본 사용 예제<pre><code class="language-java">String formattedString = Format(&quot;Hello, {0}!&quot;, &quot;World&quot;);
System.out.println(formattedString); 
// 출력: &quot;Hello, World!&quot;</code></pre>
</li>
<li>여러 파라미터와 숫자 포맷<pre><code class="language-java">String formattedString = Format(&quot;The price of {0} is {1:###,##0.00} dollars.&quot;, &quot;Apple&quot;, 1234.5);
System.out.println(formattedString); 
// 출력: &quot;The price of Apple is 1,234.50 dollars.&quot;</code></pre>
</li>
<li>날짜 포맷 사용<pre><code class="language-java">Date now = new Date();
String formattedString = Format(&quot;Today&#39;s date is {0:yyyy-MM-dd}.&quot;, now);
System.out.println(formattedString); 
// 출력: &quot;Today&#39;s date is 2024-09-12.&quot; (현재 날짜에 따라 달라질 수 있음)</code></pre>
</li>
<li>기본 형식 포맷 사용<pre><code class="language-java">String formattedString = Format(&quot;Count: {0}, Total: {1}&quot;, 100, 1234.567);
System.out.println(formattedString); 
// 출력: &quot;Count: 100, Total: 1,234.57&quot;</code></pre>
</li>
<li>다양한 데이터 타입의 포맷팅<pre><code class="language-java">String formattedString = Format(&quot;Boolean: {0}, String: {1}, Integer: {2}, Double: {3:0.00}&quot;, true, &quot;Test&quot;, 12345, 3.14159);
System.out.println(formattedString); 
// 출력: &quot;Boolean: true, String: Test, Integer: 12,345, Double: 3.14&quot;</code></pre>
</li>
<li>값이 null인 경우<pre><code class="language-java">String formattedString = Format(&quot;Name: {0}, Age: {1}&quot;, null, 30);
System.out.println(formattedString); 
// 출력: &quot;Name: null, Age: 30&quot;</code></pre>
</li>
<li>사용자 정의 포맷<pre><code class="language-java">String formattedString = Format(&quot;Sales: {0:$###,##0.00}&quot;, 1234567.89);
System.out.println(formattedString);
// 출력: &quot;Sales: $1,234,567.89&quot;</code></pre>
</li>
</ol>
<h2 id="format-getformatter-함수-동작-과정">Format, getFormatter 함수 동작 과정</h2>
<h3 id="1-빈-문자열-검사">1. 빈 문자열 검사</h3>
<p>입력 문자열 Format이 비어 있거나 null인 경우, 이를 빈 문자열로 설정합니다.</p>
<pre><code class="language-java">if (StringUtil.isEmpty(format)) {
    return &quot;&quot;;
}</code></pre>
<h3 id="2-정규-표현식을-사용한-패턴-매칭">2. 정규 표현식을 사용한 패턴 매칭</h3>
<p>정규 표현식 <code>(\\{(\\d+)(:[^\\}]+)?\\})</code>을 사용하여 형식 문자열에서 자리 표시자를 식별합니다. 이는 <code>{0}</code>, <code>{1:yyyy-MM-dd}</code>와 같은 패턴을 매칭합니다.</p>
<ul>
<li>정규 표현식 설명<ul>
<li><code>{(\\d+)(:[^\\}]+)?}</code>: 중괄호 <code>{}</code> 안의 숫자와 선택적인 형식 문자열을 매칭합니다.    </li>
<li><code>\\d+</code>: 숫자 (<code>0</code>, <code>1</code>, <code>2</code> 등)</li>
<li><code>(:[^\\}]+)?</code>: 선택적인 형식 문자열 (<code>:yyyy-MM-dd</code> 등)</li>
</ul>
</li>
</ul>
<p><code>Matcher</code> 객체를 생성하여 입력 <code>Format</code> 문자열에서 이러한 패턴을 찾습니다.</p>
<pre><code class="language-java">Pattern pattern = Pattern.compile(&quot;\\{(\\d+)(:[^\\}]+)?\\}&quot;);
Matcher matcher = pattern.matcher(format);</code></pre>
<h3 id="3-값과-형식-저장을-위한-맵-생성">3. 값과 형식 저장을 위한 맵 생성</h3>
<p><code>paramValueMap</code>과 <code>paramFormatMap</code>이라는 두 개의 <code>HashMap</code> 객체를 생성하여 매개변수 값과 해당하는 형식 포맷터를 저장합니다.</p>
<pre><code class="language-java">Map&lt;Integer, Object&gt; paramValueMap = new HashMap&lt;&gt;();
Map&lt;Integer, java.text.Format&gt; paramFormatMap = new HashMap&lt;&gt;();</code></pre>
<h3 id="4-매개변수-반복-처리">4. 매개변수 반복 처리</h3>
<p>제공된 매개변수 (<code>params</code>) 각각에 대해 반복하여 다음을 수행합니다:</p>
<ul>
<li><strong>매개변수 값 저장</strong>: <code>paramValueMap</code>에 저장합니다.</li>
<li><strong>포맷터 결정</strong>: <code>getFormatter</code> 메서드를 사용하여 매개변수에 대한 적절한 포맷터를 결정하고, 이를 paramFormatMap에 저장합니다.<pre><code class="language-java">for (int i = 0; i &lt; params.length; i++) {
  Object obj = params[i];
  paramValueMap.put(i, obj);
  paramFormatMap.put(i, obj != null ? getFormatter(obj, null) : null);
}</code></pre>
<h3 id="5-형식-문자열의-자리-표시자-처리">5. 형식 문자열의 자리 표시자 처리</h3>
형식 문자열에서 자리 표시자가 있는 동안 다음을 수행합니다</li>
</ul>
<p>패턴 문자열 (<code>{index[:format]}</code>)을 추출합니다.
<code>Convert.toInteger(matcher.group(1))</code>을 통해 자리 표시자의 인덱스를 가져옵니다.
자리 표시자에 해당하는 형식 문자열 (<code>idxFormat</code>)을 가져옵니다.
<code>paramValueMap</code>에서 인덱스에 해당하는 값을 가져옵니다.
형식 문자열이 존재하면 이를 기반으로 포맷터를 설정하고, 그렇지 않으면 기존에 설정된 기본 포맷터를 사용합니다.</p>
<pre><code class="language-java">while (matcher.find()) {
    String patternString = matcher.group();
    int idx = Convert.toInteger(matcher.group(1));
    String idxFormat = matcher.group(2);

    Object val = paramValueMap.get(idx);
    java.text.Format formatter = (idxFormat != null &amp;&amp; StringUtil.isNotEmpty(idxFormat.substring(1)))
            ? getFormatter(val, idxFormat.substring(1))
            : paramFormatMap.get(idx);</code></pre>
<h3 id="6-문자열-대체">6. 문자열 대체</h3>
<p>자리 표시자에 해당하는 값을 포맷터를 사용하여 형식화한 후, 형식 문자열의 자리 표시자를 실제 값으로 대체합니다. 포맷터가 없으면 기본 <code>toString()</code>을 사용하여 값을 문자열로 변환합니다.</p>
<pre><code class="language-java">    Object replaceValue = (val == null ? &quot;null&quot; : val);
    String replacement = (replaceValue != null &amp;&amp; formatter != null) 
                         ? formatter.format(replaceValue) 
                         : Convert.toString(replaceValue);

    int start = result.indexOf(patternString);
    if (start != -1) {
        result.replace(start, start + patternString.length(), replacement);
    }
}</code></pre>
<h3 id="7-결과-반환">7. 결과 반환</h3>
<p>형식화된 최종 문자열을 반환합니다.</p>
<pre><code class="language-java">return result.toString();</code></pre>
<h3 id="getformatter-메서드">getFormatter 메서드</h3>
<h4 id="목적">목적</h4>
<p><code>getFormatter</code> 메서드는 값의 데이터 유형과 형식 문자열 (<code>formatExp</code>)에 따라 적절한 포맷터 객체를 생성합니다.</p>
<h4 id="동작">동작</h4>
<ul>
<li><p>값의 클래스 이름에 따라 적절한 포맷터를 생성합니다.</p>
</li>
<li><p>예를 들어, <code>Date</code> 객체는 <code>SimpleDateFormat</code>을 사용하고, <code>Integer</code>, <code>Double</code>, <code>Float</code>, <code>Long</code> 등의 숫자 타입은 <code>DecimalFormat</code>을 사용합니다.</p>
</li>
<li><p><code>문자열</code>과 <code>boolean</code>의 경우, 추가적인 포맷터는 필요하지 않으므로 <code>기본값</code>으로 처리됩니다.</p>
<pre><code class="language-java">private static java.text.Format getFormatter(Object value, String formatExp) {
  if (value == null) return null;
  String className = value.getClass().getName();

  switch (className) {
      case &quot;java.util.Date&quot;:
          return StringUtil.isNotEmpty(formatExp) ? new java.text.SimpleDateFormat(formatExp) : new java.text.SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss&quot;);
      case &quot;java.lang.Integer&quot;:
          return StringUtil.isNotEmpty(formatExp) ? new DecimalFormat(formatExp) : new DecimalFormat(&quot;###,##0&quot;);
      case &quot;java.lang.Double&quot;:
      case &quot;java.lang.Float&quot;:
      case &quot;java.lang.Long&quot;:
      case &quot;java.math.BigDecimal&quot;:
          return StringUtil.isNotEmpty(formatExp) ? new DecimalFormat(formatExp) : new DecimalFormat(&quot;###,##0.##&quot;);
      default:
          return null;
  }
}</code></pre>
<h2 id="결론">결론</h2>
<p>다양한 데이터 유형과 형식화를 지원하기 위해 만들었습니다. 사용자 정의 형식 문자열을 쉽게 처리할 수 있도록 합니다. Java에서 문자열 포맷팅을 보다 유연하게 관리하고 싶다면, 이와 같은 사용자 정의 함수를 활용해 보세요!</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Mybatis]마이바티스 에러 invalid comparison: java.util.Arrays$ArrayList and java.lang.String
XML 리스트 비교연산자 오류]]></title>
            <link>https://velog.io/@oh_yunseong/Mybatis%EB%A7%88%EC%9D%B4%EB%B0%94%ED%8B%B0%EC%8A%A4-%EC%97%90%EB%9F%AC-invalid-comparison-java.util.ArraysArrayList-and-java.lang.StringXML-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EB%B9%84%EA%B5%90%EC%97%B0%EC%82%B0%EC%9E%90-%EC%98%A4%EB%A5%98</link>
            <guid>https://velog.io/@oh_yunseong/Mybatis%EB%A7%88%EC%9D%B4%EB%B0%94%ED%8B%B0%EC%8A%A4-%EC%97%90%EB%9F%AC-invalid-comparison-java.util.ArraysArrayList-and-java.lang.StringXML-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EB%B9%84%EA%B5%90%EC%97%B0%EC%82%B0%EC%9E%90-%EC%98%A4%EB%A5%98</guid>
            <pubDate>Thu, 05 Sep 2024 15:00:21 GMT</pubDate>
            <description><![CDATA[<p>왜 mybatis를 안쓰는지 참 이해가 간다. 
디버깅이 어려우며, 컴파일 당시에 에러를 잡지 못한다. 그리고 쿼리 돌릴때 예외가 참 많다.</p>
<p>이번에 닥친 오류는 </p>
<blockquote>
<p>invalid comparison: java.util.Arrays$ArrayList and java.lang.String
XML 리스트 비교연산자 오류</p>
</blockquote>
<p>리스트 형태의 파라미터를 !=, == 연산자로 리스트의 공백여부를 비교하려고 했기 때문이다.</p>
<p>다른 글들에서는 파라미터명.size() != 0 으로 하라고 하는데 
파라미터명.isEmpty()도 가능하다. 
참고로 .equals(&#39;&#39;) =&gt; 이것도 오류는 안나지만, 옳은 비교가 아니다(의미없는 비교). 이유는 리스트 객체와 &#39;&#39; 값을 비교하기 때문. 늘 false를 반환한다.</p>
<pre><code>&lt;select id=&quot;selectItems&quot; parameterType=&quot;java.util.List&quot;&gt;
  SELECT * FROM items
  WHERE item_id NOT IN
  &lt;if idList != null and !idList.isEmpty()&gt;
    &lt;foreach collection=&quot;idList&quot; item=&quot;id&quot; index=&quot;index&quot;  open=&quot;(&quot; separator=&quot;,&quot; close=&quot;)&quot;&gt;
      #{id}
    &lt;/foreach&gt;
  &lt;/if&gt;
&lt;/select&gt;
&lt;!--혹은 --&gt;
&lt;select id=&quot;selectItems&quot; parameterType=&quot;java.util.List&quot;&gt;
  SELECT * FROM items
  WHERE item_id NOT IN
  &lt;if idList != null and idList.size != 0&gt;
    &lt;foreach collection=&quot;idList&quot; item=&quot;id&quot; index=&quot;index&quot;  open=&quot;(&quot; separator=&quot;,&quot; close=&quot;)&quot;&gt;
      #{id}
    &lt;/foreach&gt;
  &lt;/if&gt;
&lt;/select&gt;</code></pre><h2 id="요약">요약</h2>
<p>SQL에서는 != 연산자가 단일 값과의 비교에 사용되며, 리스트와 같은 컬렉션 형태와 비교할 수 없습니다. 따라서, MyBatis에서 리스트 형태의 파라미터를 사용하여 != 조건을 작성하려고 하면 SQL 문법 오류가 발생합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS]map, filter, reduce 등 다양한 배열메서드 개념 및 활용법]]></title>
            <link>https://velog.io/@oh_yunseong/JSmap-filter-reduce-%EB%93%B1-%EB%8B%A4%EC%96%91%ED%95%9C-%EB%B0%B0%EC%97%B4%EB%A9%94%EC%84%9C%EB%93%9C-%EA%B0%9C%EB%85%90-%EB%B0%8F-%ED%99%9C%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@oh_yunseong/JSmap-filter-reduce-%EB%93%B1-%EB%8B%A4%EC%96%91%ED%95%9C-%EB%B0%B0%EC%97%B4%EB%A9%94%EC%84%9C%EB%93%9C-%EA%B0%9C%EB%85%90-%EB%B0%8F-%ED%99%9C%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Thu, 05 Sep 2024 10:09:42 GMT</pubDate>
            <description><![CDATA[<h3 id="개념">개념</h3>
<p>배열을 조작하고 변환하기 위한 고차 함수(higher-order function)로, 배열의 각 요소에 대해 특정 작업을 수행하는 데 사용됩니다. 이러한 함수들은 JavaScript의 배열 메서드로, 순회(iteration), 필터링(filtering), 변환(transformation) 등의 작업을 쉽게 수행할 수 있도록 도와줍니다. 이들은 함수형 프로그래밍의 개념을 지원하며, 코드를 더 간결하고 읽기 쉽게 만들어줍니다.</p>
<h4 id="1-map-메서드">1. map 메서드</h4>
<p>개념: map은 배열의 각 요소에 대해 제공된 함수를 호출하고, 그 결과를 모아 새로운 배열을 생성합니다. 원래 배열을 변경하지 않고, 항상 원본 배열과 동일한 길이의 새로운 배열을 반환합니다.
사용 목적: 배열의 모든 요소에 동일한 작업을 수행해야 할 때 사용합니다. 예를 들어, 숫자 배열의 모든 요소에 2를 곱하거나, 문자열 배열의 모든 요소를 대문자로 변환할 때 유용합니다.</p>
<ul>
<li>예제<pre><code>const numbers = [1, 2, 3, 4, 5];
const doubledNumbers = numbers.map((number) =&gt; number * 2);
console.log(doubledNumbers); // [2, 4, 6, 8, 10]</code></pre>이 예제에서 map은 number * 2라는 함수를 배열의 각 요소에 적용하고, 그 결과를 doubledNumbers라는 새로운 배열에 저장합니다.</li>
</ul>
<h4 id="2-filter-메서드">2. filter 메서드</h4>
<p>개념: filter는 배열의 각 요소에 대해 제공된 함수를 호출하고, 함수가 true를 반환하는 요소들만 모아 새로운 배열을 생성합니다. 원래 배열을 변경하지 않고, 조건에 맞는 요소들로만 구성된 새로운 배열을 반환합니다.
사용 목적: 배열에서 특정 조건을 만족하는 요소들만 추출하고자 할 때 사용합니다. 예를 들어, 짝수만 추출하거나, 특정 문자열을 포함하는 요소만 걸러낼 때 유용합니다.
예제</p>
<pre><code>const numbers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter((number) =&gt; number % 2 === 0);
console.log(evenNumbers); // [2, 4]</code></pre><p>이 예제에서 filter는 number % 2 === 0이라는 함수를 배열의 각 요소에 적용하고, 짝수만 evenNumbers라는 새로운 배열에 저장합니다.</p>
<ol start="3">
<li>그 외 자주 사용되는 배열 메서드
JavaScript에는 map과 filter 외에도 배열을 다루기 위한 다양한 메서드가 있습니다.</li>
</ol>
<h4 id="3-reduce-메서드">3. reduce 메서드</h4>
<p>개념: reduce는 배열의 각 요소를 순회하며 누적 결과를 계산합니다. 누적 결과는 배열의 모든 요소를 함수에 적용한 결과로, 단일 값이 됩니다. 이 메서드는 배열을 단일 값으로 줄이는 데 유용합니다.
사용 목적: 배열의 합계, 곱셈, 평균 등을 계산하거나 객체를 그룹화할 때 사용합니다.
예제</p>
<pre><code>const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((accumulator, currentValue) =&gt; accumulator + currentValue, 0);
console.log(sum); // 15</code></pre><p>이 예제에서 reduce는 accumulator + currentValue라는 함수를 배열의 각 요소에 적용하여 전체 요소의 합을 계산합니다.</p>
<h4 id="4-foreach-메서드">4. forEach 메서드</h4>
<p>개념: forEach는 배열의 각 요소에 대해 주어진 함수를 호출하지만, 반환 값이 없으며 새 배열을 생성하지 않습니다.
사용 목적: 배열의 각 요소에 대해 어떤 작업을 수행해야 하지만, 결과가 필요하지 않은 경우 사용합니다. 예를 들어, 배열의 요소를 콘솔에 출력하거나 DOM 조작을 수행할 때 유용합니다.
예제</p>
<pre><code>
const numbers = [1, 2, 3, 4, 5];
numbers.forEach((number) =&gt; {
  console.log(number);
});</code></pre><p>이 예제에서 forEach는 배열의 각 요소를 순회하며 콘솔에 출력합니다.</p>
<h4 id="5-find-메서드">5. find 메서드</h4>
<p>개념: find는 배열에서 주어진 조건을 만족하는 첫 번째 요소를 반환합니다. 조건을 만족하는 요소가 없으면 undefined를 반환합니다.
사용 목적: 배열에서 특정 조건을 만족하는 첫 번째 요소를 찾을 때 사용합니다.
예제</p>
<pre><code>const numbers = [1, 2, 3, 4, 5];
const firstEvenNumber = numbers.find((number) =&gt; number % 2 === 0);
console.log(firstEvenNumber); // 2</code></pre><p>이 예제에서 find는 조건 number % 2 === 0을 만족하는 첫 번째 요소(짝수)인 2를 반환합니다.</p>
<h4 id="6-some-메서드">6. some 메서드</h4>
<p>개념: some은 배열의 요소 중 하나라도 주어진 조건을 만족하는지 검사하여, 만족하면 true, 만족하지 않으면 false를 반환합니다.
사용 목적: 배열에 특정 조건을 만족하는 요소가 하나라도 있는지 검사할 때 사용합니다.
예제</p>
<pre><code>const numbers = [1, 2, 3, 4, 5];
const hasEvenNumber = numbers.some((number) =&gt; number % 2 === 0);
console.log(hasEvenNumber); // true</code></pre><p>이 예제에서 some은 배열에 짝수가 하나라도 있는지 검사하고, true를 반환합니다.</p>
<h4 id="7-every-메서드">7. every 메서드</h4>
<p>개념: every는 배열의 모든 요소가 주어진 조건을 만족하는지 검사하여, 모두 만족하면 true, 하나라도 만족하지 않으면 false를 반환합니다.
사용 목적: 배열의 모든 요소가 특정 조건을 만족하는지 확인할 때 사용합니다.
예제</p>
<pre><code>const numbers = [1, 2, 3, 4, 5];
const allEvenNumbers = numbers.every((number) =&gt; number % 2 === 0);
console.log(allEvenNumbers); // false</code></pre><p>이 예제에서 every는 배열의 모든 요소가 짝수인지 검사하고, 하나라도 짝수가 아니기 때문에 false를 반환합니다.</p>
<h3 id="결론">결론</h3>
<p>JavaScript의 배열 메서드(map, filter, reduce, forEach, find, some, every 등)는 배열 요소를 조작하고 변환하는 작업을 간결하고 직관적으로 수행할 수 있도록 도와줍니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS]setTimeout이란? 활용법과 딜레이 시간 파라미터를 지정하지 않는 경우?]]></title>
            <link>https://velog.io/@oh_yunseong/JSsetTimeout%EC%9D%B4%EB%9E%80-%ED%99%9C%EC%9A%A9%EB%B2%95%EA%B3%BC-%EB%94%9C%EB%A0%88%EC%9D%B4-%EC%8B%9C%EA%B0%84-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0%EB%A5%BC-%EC%A7%80%EC%A0%95%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EA%B2%BD%EC%9A%B0</link>
            <guid>https://velog.io/@oh_yunseong/JSsetTimeout%EC%9D%B4%EB%9E%80-%ED%99%9C%EC%9A%A9%EB%B2%95%EA%B3%BC-%EB%94%9C%EB%A0%88%EC%9D%B4-%EC%8B%9C%EA%B0%84-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0%EB%A5%BC-%EC%A7%80%EC%A0%95%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EA%B2%BD%EC%9A%B0</guid>
            <pubDate>Mon, 02 Sep 2024 13:52:40 GMT</pubDate>
            <description><![CDATA[<p>이제까지 비동기적으로 실행되는 자바스크립트에서 동기화처리를 하기 위해 Promise를 활용했었다.
setTimeout은 뭔가 임시방편 같은 느낌이었는데, 개발을 하다가 확실히 간단한 동기화처리에는 setTimeout이 효율적인 방법이란 걸 알게 됐다.</p>
<h3 id="settimeout이란">setTimeout이란?</h3>
<ul>
<li>일정 시간이 지난 후에 특정 함수를 실행하도록 예약하는 메서드<h3 id="코드">코드</h3>
<pre><code>setTimeout(function, delay, ...args)</code></pre></li>
<li>function: 지연 시간 후에 실행될 함수.</li>
<li>delay: 지연 시간(밀리초 단위, 1000ms = 1초).</li>
<li>...args: 실행될 함수에 전달될 선택적 인수.</li>
</ul>
<pre><code>setTimeout(() =&gt; {
    console.log(&quot;3초 후에 이 메시지가 출력됩니다.&quot;);
}, 3000); </code></pre><ul>
<li>위처럼 콜백함수로 바로 작성하여 개발하는 경우가 많다. <pre><code>function sum(a, b) {
  console.log(a + b);
}
</code></pre></li>
</ul>
<p>setTimeout(sum, 1000, 5, 10); // 1초 후에 &quot;15&quot;가 출력됩니다.</p>
<pre><code>- 또, 스크립트에 이미 개발해놓은 함수에 파라미터를 추가하여 호출 및 전달하여도 된다.

### setTimeout을 동기화처리에 활용
- 비동기처리는 다양한 이유(실시간데이터처리, 병렬작업, api호출, 이벤트리스너)로 사용자경험을 개선하는데 도움을 줍니다.
- api호출 후 코드들을 동기화처리 하기 위해서 간단하게 setTimeout을 활용할 수 있습니다.
- 이 때, 특정 밀리초를 파라미터로 전달하게 되면 api응답 전에 지정한 시간이 지나버려 꼬여버릴 수 있습니다.
- 시간 **파라미터를 0 혹은 생략하게 되면, 비동기 API 호출을 수행한 이후의 작업이 순차적으로 실행됩니다.**
- 예시</code></pre><p>  function fetchData(url, callback) {
    console.log(&quot;API 호출 시작...&quot;);</p>
<pre><code>// 비동기 API 호출
fetch(url)
  .then(response =&gt; response.json())
  .then(data =&gt; {
    console.log(&quot;API 호출 완료. 데이터:&quot;, data);
  })
  .catch(error =&gt; console.error(&quot;API 호출 에러:&quot;, error));
//이후 setTimeout호출로 api호출이 끝난 후 callback함수실행
setTimeout(() =&gt; {
   callback(data);
}, 0); // 0ms의 지연 시간으로 콜백 호출</code></pre><p>  }</p>
<pre><code></code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[📌 코딩테스트 준비 :: 백준_11651:: 정렬 - 좌표정렬하기2👀]]></title>
            <link>https://velog.io/@oh_yunseong/%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A4%80%EB%B9%84-%EB%B0%B1%EC%A4%8011651-%EC%A0%95%EB%A0%AC-%EC%A2%8C%ED%91%9C%EC%A0%95%EB%A0%AC%ED%95%98%EA%B8%B02</link>
            <guid>https://velog.io/@oh_yunseong/%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A4%80%EB%B9%84-%EB%B0%B1%EC%A4%8011651-%EC%A0%95%EB%A0%AC-%EC%A2%8C%ED%91%9C%EC%A0%95%EB%A0%AC%ED%95%98%EA%B8%B02</guid>
            <pubDate>Thu, 24 Feb 2022 14:49:05 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p><img src="https://images.velog.io/images/oh_yunseong/post/b5661512-bfee-4b38-9b66-31ebf2e0200b/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-24%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.46.12.png" alt=""></p>
<h2 id="풀이">풀이</h2>
<ol>
<li>comparator 만들어서 풀어주면됩니다.</li>
<li>익명클래스이기 때문에 람다식으로 오버라이드 메소드를 간단하게 구현해줬습니다.</li>
</ol>
<h2 id="코드">코드</h2>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;


public class Bj11651_AlignCoordinate2 {
    public static void main(String[] args) throws IOException {
        BufferedReader br  = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        int[][] arr = new int[n][2];
        for(int i = 0; i&lt; n; i++) {
            String[] s = br.readLine().split(&quot; &quot;);
            arr[i][0] = Integer.parseInt(s[0]);
            arr[i][1] = Integer.parseInt(s[1]);
        }

        Arrays.sort(arr,(e1,e2) -&gt; {
            if(e1[1] ==e2[1]) {
                return e1[0] - e2[0];
            } else {
                return e1[1] - e2[1];
            }
        });
        for(int i = 0 ; i &lt; n ; i ++){
            System.out.println(arr[i][0] + &quot; &quot; + arr[i][1]);
        }
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[📌 코딩테스트 준비 :: 백준_18310:: 정렬 - 안테나👀]]></title>
            <link>https://velog.io/@oh_yunseong/%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A4%80%EB%B9%84-%EB%B0%B1%EC%A4%8018310-%EC%A0%95%EB%A0%AC-%EC%95%88%ED%85%8C%EB%82%98</link>
            <guid>https://velog.io/@oh_yunseong/%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A4%80%EB%B9%84-%EB%B0%B1%EC%A4%8018310-%EC%A0%95%EB%A0%AC-%EC%95%88%ED%85%8C%EB%82%98</guid>
            <pubDate>Thu, 24 Feb 2022 14:36:42 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p><img src="https://images.velog.io/images/oh_yunseong/post/ee7bdf93-a456-4cff-82ce-e1020ad7b1ba/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-24%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.35.07.png" alt=""></p>
<h2 id="풀이">풀이</h2>
<ol>
<li>가운데 있는 게 가장 효율적입니다. </li>
<li>배열의 가운데 값을 제출 해주면 됩니다.</li>
</ol>
<h2 id="코드">코드</h2>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;

public class Bj18310_antenna {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        String[] s = br.readLine().split(&quot; &quot;);
        int[] arr = new int[n];
        for (int i = 0; i&lt; n ; i++){
            arr[i] = Integer.parseInt(s[i]);
        }
        Arrays.sort(arr);
        System.out.println(arr[(n-1)/2]);
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[📌 코딩테스트 준비 :: 백준_13164:: 정렬 - 행복유치원👀]]></title>
            <link>https://velog.io/@oh_yunseong/%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A4%80%EB%B9%84-%EB%B0%B1%EC%A4%8013164-%EC%A0%95%EB%A0%AC-%ED%96%89%EB%B3%B5%EC%9C%A0%EC%B9%98%EC%9B%90</link>
            <guid>https://velog.io/@oh_yunseong/%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A4%80%EB%B9%84-%EB%B0%B1%EC%A4%8013164-%EC%A0%95%EB%A0%AC-%ED%96%89%EB%B3%B5%EC%9C%A0%EC%B9%98%EC%9B%90</guid>
            <pubDate>Thu, 24 Feb 2022 14:34:15 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p><img src="https://images.velog.io/images/oh_yunseong/post/9456d8ae-f760-41f3-adc3-9c839144f4f1/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-24%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.13.16.png" alt=""></p>
<h2 id="풀이">풀이</h2>
<ol>
<li>어떻게 묶어서 차이를 계산할지 보다는 나열해서 일정한 규칙이 나오는지 확인해보면 됩니다.</li>
<li>처음에는 수식을 어떻게 세워야할까 고민했습니다.</li>
<li>최소를 구하는 것이기 때문에 각 숫자간 차이를 정렬합니다.</li>
<li>k묶음에서 k가 커질수록 한개 = 한묶음이 많아져서 최소 숫자가 작아집니다. </li>
<li>이규칙을 활용해 k묶음일 때 각 숫자간 차이를 더해서 출력해주면 됩니다.</li>
</ol>
<h2 id="코드">코드</h2>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Bj13164_happy {


    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;
        st = new StringTokenizer(br.readLine());
        //입력값 받기
        int n = Integer.parseInt(st.nextToken());
        int k = Integer.parseInt(st.nextToken());
        //원생 초기화
        int[] arr = new int[n];
        st = new StringTokenizer(br.readLine());
        int cnt = 0;
        while(st.hasMoreTokens()) {
            arr[cnt] = Integer.parseInt(st.nextToken());
            cnt++;
        }
        //숫자간 차이 초기화 
        int[] answer = new int[n];
        answer[0]=0;
        for(int i = 1 ; i &lt; n ; i++) {
            answer[i] = arr[i]- arr[i-1];
        }
        Arrays.sort(answer);
        int sum = 0;
        for(int i = 0 ; i &lt; n-k+1 ; i++) {
            sum += answer[i];
        }
        System.out.println(sum);

        //예를들어.. n = 5개임
        //-&gt; 1개로 묶을 시 n, 0 1 2 2 4
        //-&gt; 2개로 묶을 시 n - 1, 0 1 2 2
        //-&gt; 3개로 묶을 시 n - 2, 0 1 2
        //-&gt; 4개로 묶을 시 n - 3, 0 1
        //-&gt; 5개로 묶을 시 n - 4, 0

    }

}
</code></pre>
]]></description>
        </item>
    </channel>
</rss>