<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>choisooyoung-dev.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 09 Apr 2024 12:04:44 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>choisooyoung-dev.log</title>
            <url>https://velog.velcdn.com/images/choisooyoung-dev/profile/c57b0ab2-cfea-47e7-a6dd-bfb7c179c0a3/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. choisooyoung-dev.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/choisooyoung-dev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[TIL] 메모리]]></title>
            <link>https://velog.io/@choisooyoung-dev/TIL-%EB%A9%94%EB%AA%A8%EB%A6%AC</link>
            <guid>https://velog.io/@choisooyoung-dev/TIL-%EB%A9%94%EB%AA%A8%EB%A6%AC</guid>
            <pubDate>Tue, 09 Apr 2024 12:04:44 GMT</pubDate>
            <description><![CDATA[<h1 id="메모리-셀">메모리 셀</h1>
<p><img src="https://velog.velcdn.com/images/choisooyoung-dev/post/c194e332-0457-43e0-ac30-305803758609/image.png" alt=""></p>
<ul>
<li>각각의 메모리 셀은 1byte</li>
<li>1byte 이상을 쓰려면 여러개의 메모리 셀을 쓰면 된다.</li>
<li>메모리 셀들은 각각 메모리 주소가 존재한다.</li>
<li>메모리 주소 값들을 일일이 기억할 수 없으니 변수라는 것을 정해서 쓰는 것</li>
<li>변수는 메모리의 주소를 가리킨다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DevCamp - 4, 5일차, 수료] PG사(토스페이먼츠) 연결, 배포]]></title>
            <link>https://velog.io/@choisooyoung-dev/DevCamp-4-5%EC%9D%BC%EC%B0%A8-%EC%88%98%EB%A3%8C-PG%EC%82%AC%ED%86%A0%EC%8A%A4%ED%8E%98%EC%9D%B4%EB%A8%BC%EC%B8%A0-%EC%97%B0%EA%B2%B0-%EB%B0%B0%ED%8F%AC</link>
            <guid>https://velog.io/@choisooyoung-dev/DevCamp-4-5%EC%9D%BC%EC%B0%A8-%EC%88%98%EB%A3%8C-PG%EC%82%AC%ED%86%A0%EC%8A%A4%ED%8E%98%EC%9D%B4%EB%A8%BC%EC%B8%A0-%EC%97%B0%EA%B2%B0-%EB%B0%B0%ED%8F%AC</guid>
            <pubDate>Fri, 29 Mar 2024 08:46:00 GMT</pubDate>
            <description><![CDATA[<h2 id="쿠폰과-유저-관계설정">쿠폰과 유저 관계설정</h2>
<ul>
<li>유저와 쿠폰의 관계가 1:N이 아닌 N:N이므로 중간 테이블 user_coupons 생성</li>
<li>TypeORM상에서 @JoinColumn() 사용하여 중간 테이블 로직을 처음 구현해보았다.<h2 id="회원가입시-가입-쿠폰-등록">회원가입시 가입 쿠폰 등록</h2>
</li>
<li>register 이름을 가진 쿠폰들을 가입시 유저의 아이디와 각 회원가입 전용 쿠폰의 아이디가 등록되도록 구현</li>
</ul>
<h3 id="보완해야할-점">보완해야할 점</h3>
<ul>
<li>쿠폰의 이름으로 구분하는 것이 아닌 다른 방식 요함.</li>
<li>내가 생각했던건<ul>
<li>쿠폰 이름: 회원    가입 선물 30% 할인쿠폰</li>
<li>쿠폰 종류: register</li>
</ul>
</li>
</ul>
<h2 id="포인트">포인트</h2>
<ul>
<li>결제 후 몇퍼센트의 포인트를 적립하는 방식</li>
<li>결제 로직을 만든 후 적용해봐야할듯</li>
</ul>
<h2 id="pg사토스-페이먼트-연결">PG사(토스 페이먼트) 연결</h2>
<h3 id="토스페이먼츠-연결-흐름-파악하기">토스페이먼츠 연결 흐름 파악하기</h3>
<ol>
<li>Redirect</li>
</ol>
<ul>
<li>결제 요청시 차례대로 인증과 승인 과정 진행</li>
<li>인증: 결제를 승인하기 전 결제 정보가 올바른지 검증</li>
<li>승인: 인증에 성공한 결제를 최종 승인</li>
<li>승인 요청 성공 -&gt; 결제 요청 과정 끝</li>
</ul>
<p><code>인증 성공</code></p>
<ul>
<li>요청 결과에 따라 리다이렉트 된다.</li>
</ul>
<p><code>인증 실패</code></p>
<ul>
<li>실패 리다이렉트에 쿼리 파라미터로 담긴 에러 정보 나옴</li>
</ul>
<h2 id="회고">회고</h2>
<blockquote>
<p>모든 미션을 수행하지 못했고, 오늘 배운 점에는 보완할 점만 가득 적어서 제출했었다.
데브캠프에서 내가 정한 목표는 내가 보완해야할 점과 배운것을 정리해서 빠짐없이 제출하자였다, 그래도 그 목표는 이뤘으며 차차 보완해야할 점과 구현 못한 부분을 계속해서 구현해봐야겠다.
이번 데브캠프 내내 nest.js 공식문서를 많이 붙잡고 보려고 했다. 상당한 부분이 기억에 남아있지 않았고,, 매순간이 반성의 순간이었다 아직도 익숙치 않은 공식문서이지만 결국은 공식문서가 해결책이었기 때문에 공식문서를 참고하는 것은 습관처럼 돼야한다.
반성을 하면서도 내일배움캠프 수료후 스스로가 굉장히 해이해졌다는 생각도 든다. 정해진 시간내에 몰입할 수 있게끔 방법을 찾아야겠다.
노력의 댓가로 우수 수료생으로 뽑혀 외주 개발의 기회가 주어졌다!! 아직 어떤걸 할지는 모르지만 아주 기대가 된다 ㅎㅎㅎ</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DevCamp - 2주 3일차] - 포인트 API 구현]]></title>
            <link>https://velog.io/@choisooyoung-dev/DevCamp-2%EC%A3%BC-3%EC%9D%BC%EC%B0%A8-%ED%8F%AC%EC%9D%B8%ED%8A%B8-API-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@choisooyoung-dev/DevCamp-2%EC%A3%BC-3%EC%9D%BC%EC%B0%A8-%ED%8F%AC%EC%9D%B8%ED%8A%B8-API-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Wed, 27 Mar 2024 17:05:09 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>쿠폰 API 개발 전 포인트 API 구현하려고 했으나, 앞에 회원부분에 미련이 남았고, 레디스를 도입하여 리프레시 토큰을 관리하는 방식으로 채택해 토큰 블랙리스트를 구현해보자가 목표로 바뀌었다.
시간이 정해져있는 프로젝트이지만 온전히 집중할 수 없는 상황이므로(이력서, 포폴, 면접 등 준비할게 태산..게다가 nest.js, typescript등 지식의 부재) 미션과 상관없이 혼자서 계속 구상하며 디벨롭 해야겠다는 생각뿐이다.</p>
</blockquote>
<h3 id="오늘-구현한-부분">오늘 구현한 부분</h3>
<ul>
<li>로그인 기본 기능 구현</li>
<li>쿠폰, 포인트 관련 엔티티 추가</li>
<li>각각 사용자와 관계설정 <h3 id="보완해야할-부분">보완해야할 부분</h3>
</li>
<li>레디스에 리프레시 토큰 넣기</li>
<li>로그아웃</li>
</ul>
<h2 id="redis를-연결해주면서-ioredis를-알게되었다">redis를 연결해주면서 ioredis를 알게되었다.</h2>
<ul>
<li>Node.js 환경에서 Redis를 사용하기 위한 강력하고 성능이 뛰어난 Redis 클라이언트 라이브러리라고 
구체적으로 빠른 성능을 제공하며 높은 I/O 처리량을 필요로 하는 애플리케이션에 적합하며 레디스 서버 장애시 자동으로 재연결을 시도하고 비동기 작업을 더욱 편리하게 처리할 수 있게끔 프로미스를 지원</li>
<li>nest.js에서 레디스를 사용하려고 검색해봤을때 cache-manager를 쓰는 방법도 있었-다.</li>
<li>ioredis는 Redis와의 직접적인 통신과 관련된 작업에 더 특화되어 있으며, cache-manager는 다양한 캐시 백엔드를 지원하는 보다 범용적인 캐싱 솔루션-</li>
<li>지금 현 프로젝트에서는 캐싱의 필요성을 못느꼈으므로 ioredis 방식 채택</li>
</ul>
<h2 id="포인트-쿠폰관련-정책">포인트, 쿠폰관련 정책</h2>
<ul>
<li>CTO님 발제 내용중에 회사의 입장에서의 정책 내용을 들었을때 처음 알게된 내용정리</li>
</ul>
<h3 id="전체-시스템-고려백">전체 시스템 고려(백)</h3>
<ul>
<li>쿠폰의 발행량을 제한 둘것인가</li>
<li>매출에 따라서 쿠폰의 발행양을 자동 조정 하도록 할건가</li>
<li>가이드 라인이 노출되도록 할건가</li>
<li>api를 열어두고 벌크 발행이 가능하게 할것인가</li>
<li>알림톡을 할것인가</li>
<li>사용한 기록을 어떻게 남길것인가</li>
<li>발급을 해놓고 만료된 쿠폰은 어떻게 처리할것인가</li>
<li>포인트는 고객과 회사의 자산</li>
</ul>
<p>단순히 고객의 입장에서 구상했을때와 달리 개발자의 입장으로 바라본 정책을 생각해보는 방법도 알아봐야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DevCamp - 2주 1일차, 2일차] 쿠폰, 포인트, 결제 로직 구상하기, 쿠폰 API 구현]]></title>
            <link>https://velog.io/@choisooyoung-dev/DevCamp-2%EC%A3%BC-1%EC%9D%BC%EC%B0%A8-2%EC%9D%BC%EC%B0%A8-%EC%BF%A0%ED%8F%B0-%ED%8F%AC%EC%9D%B8%ED%8A%B8-%EA%B2%B0%EC%A0%9C-%EB%A1%9C%EC%A7%81-%EA%B5%AC%EC%83%81%ED%95%98%EA%B8%B0-%EC%BF%A0%ED%8F%B0-API-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@choisooyoung-dev/DevCamp-2%EC%A3%BC-1%EC%9D%BC%EC%B0%A8-2%EC%9D%BC%EC%B0%A8-%EC%BF%A0%ED%8F%B0-%ED%8F%AC%EC%9D%B8%ED%8A%B8-%EA%B2%B0%EC%A0%9C-%EB%A1%9C%EC%A7%81-%EA%B5%AC%EC%83%81%ED%95%98%EA%B8%B0-%EC%BF%A0%ED%8F%B0-API-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Wed, 27 Mar 2024 02:00:36 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>쿠폰 구현까지는 하지 못했다,, 당장 쿠폰을 어떻게 적용시킬까 고민하다가 포인트를 해결해야 쿠폰도 가능하다 싶었고, 사람별로 동작되는걸 확인해야하니까 회원 로직을 다시 구상하였다.</p>
</blockquote>
<p>전체 시스템 고려(백)</p>
<ul>
<li><p>쿠폰의 발행량을 제한 둘것인가</p>
</li>
<li><p>매출에 따라서 쿠폰의 발행양을 자동 조정 하도록 할건가</p>
</li>
<li><p>가이드 라인이 노출되도록 할건가</p>
</li>
<li><p>api를 열어두고 벌크 발행이 가능하게 할것인가</p>
</li>
<li><p>알림톡을 할것인가</p>
</li>
<li><p>사용한 기록을 어떻게 남길것인가</p>
</li>
<li><p>발급을 해놓고 만료된 쿠폰은 어떻게 처리할것인가</p>
</li>
<li><p>내가 할 수 있는 것 생각해보기</p>
<ul>
<li>쿠폰 만료시간 체크</li>
<li>쿠폰 사용 기록 체크</li>
<li>쿠폰 정률제 vs 정액제</li>
<li>쿠폰 가격보다 원가가 크다면?</li>
</ul>
</li>
</ul>
<p><strong>목적</strong></p>
<ul>
<li>예시코드를 보며 결제 관련 비즈니스 로직을 습득</li>
<li>직접 PG사 연결</li>
<li>정액제, 정률제 쿠폰 적용</li>
<li>토큰 블랙리스트 방식 적용</li>
</ul>
<p><strong>구현 기능</strong></p>
<ul>
<li>로그인</li>
<li>회원가입</li>
<li>로그아웃</li>
<li>JWT 토큰 인증, 인가</li>
<li>JWT 토큰 블랙리스트 추가</li>
<li>PG사 결제 연동</li>
<li>쿠폰 적용가 결제</li>
</ul>
<p><strong>구상 로직</strong></p>
<ul>
<li>회원가입시 30% 할인 쿠폰과 5000원 할인 쿠폰을 지급</li>
<li>특정 물건 구입시 원하는 쿠폰을 선택</li>
<li>해당 쿠폰에 따라 가격을 조정 후 결제</li>
<li>Admin은 물건을 사고 팔 수 있다.</li>
</ul>
<p><strong>최초 로그인</strong></p>
<ul>
<li>로그인 요청</li>
<li>access token, refresh token 생성 및 반환, refresh token redis에 저장</li>
<li>access token 세션 스토리지, refresh token 쿠키에 저장</li>
</ul>
<p><strong>클라이언트 요청시</strong></p>
<ul>
<li>access token 유효기간 확인<ul>
<li>유효 토큰: header에 담아 요청보냄</li>
<li>유효하지 않은 토큰: refresh token header에 담아 acces token 재발급 요청한다.(refresh 유효하면 access token 재발급, 재저장 후 요청 다시 보내기)</li>
</ul>
</li>
</ul>
<p><strong>로그아웃</strong></p>
<ul>
<li>로그아웃 요청</li>
<li>세션에 있는 access token 삭제</li>
<li>redis 저장된 refresh 삭제, 해당 access token을 redis black list에 추가(이때 access token의 남은 유효 기간만큼 설정하여 저장해준다.)</li>
<li>사용자가 서비스 사용을 끝냈지만, 아직 유효기간이 끝나지않은 토큰을 Redis의 블랙리스트에 저장하고, 모든 클라이언트 요청이 들어올 때 Redis의 블랙리스트를 조회한다.</li>
<li>블랙리스트에 존재하는 토큰으로 인증 시도시 거부</li>
</ul>
<p><strong>Coupon 구상 로직</strong></p>
<ul>
<li>회원가입시 해당 유저에게 30% 할인 쿠폰과 5000원 할인 쿠폰 지급</li>
<li></li>
</ul>
<p><strong>구상 DB</strong></p>
<ul>
<li>회원</li>
</ul>
<pre><code>id, userName, password, phone, role(Admin | user), couponId(FK)
</code></pre><ul>
<li>상품</li>
</ul>
<pre><code>id, productName, price, isSoldOut, userId(FK), createdAt, updatedAt
</code></pre><ul>
<li>쿠폰</li>
</ul>
<pre><code>id, couponName, applyPrice, applyPercentage, expiredAt, couponType(price | percent), userId(PK)
</code></pre><ul>
<li>결제</li>
</ul>
<pre><code>id, total_price, userId(FK), productId(FK), createdAt, isAccept
</code></pre><p><strong>기술 스택</strong></p>
<ul>
<li>Typescript, Nest.js, PostgreSQL, TypeORM</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DevCamp - 5일차] 로그인/회원가입 API 배포]]></title>
            <link>https://velog.io/@choisooyoung-dev/DevCamp-5%EC%9D%BC%EC%B0%A8-%EB%A1%9C%EA%B7%B8%EC%9D%B8%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85-API-%EB%B0%B0%ED%8F%AC</link>
            <guid>https://velog.io/@choisooyoung-dev/DevCamp-5%EC%9D%BC%EC%B0%A8-%EB%A1%9C%EA%B7%B8%EC%9D%B8%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85-API-%EB%B0%B0%ED%8F%AC</guid>
            <pubDate>Sun, 24 Mar 2024 17:28:58 GMT</pubDate>
            <description><![CDATA[<p>Token blacklist</p>
<ul>
<li>로그아웃 로직을 생각하던 중 리드미 통해서 알게 된 개념입니다.</li>
<li>액세스 토큰 만료 전 로그아웃 요청이 생길 경우 해당 액세스 토큰을 만료 시킬 수 없으니 블랙 리스트에 추가해 접근을 막는 용도입니다.</li>
<li>제공해주신 로직 통해서 로그아웃을 구현해보려고 했으나 아직 정확하게 해당 코드에 대해 이해하지 못해 실패했습니다.</li>
<li>액세스 토큰은 db에 저장할 경우 stateless가 지켜지지 않을 수 있다는 생각이 들었습니다.</li>
<li><blockquote>
<p>좀 더 구체적으로 알아볼 사항입니다.</p>
</blockquote>
</li>
</ul>
<p>배포</p>
<ul>
<li>pem 파일로 배포 시도 중 권한 관련 에러가 나왔습니다.</li>
<li>chmod 400 명령어로 권한 에러를 해결하였습니다.</li>
<li>pm2 사용해 무중단 배포하였습니다. </li>
</ul>
<p>예외처리</p>
<ul>
<li>HttpExeption 사용하여 하였으나, 에러처리에 관해 따로 관리할 수 있도록 디벨롭 할 예정입니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DevCamp - 4일차] 로그인 API 구현]]></title>
            <link>https://velog.io/@choisooyoung-dev/DevCamp-4%EC%9D%BC%EC%B0%A8-%EB%A1%9C%EA%B7%B8%EC%9D%B8-API-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@choisooyoung-dev/DevCamp-4%EC%9D%BC%EC%B0%A8-%EB%A1%9C%EA%B7%B8%EC%9D%B8-API-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Thu, 21 Mar 2024 17:18:00 GMT</pubDate>
            <description><![CDATA[<h3 id="jti">jti?</h3>
<ul>
<li>jwt 고유 식별자 → 중복처리방지용</li>
</ul>
<h3 id="class-가-아닌-type으로-선언">class 가 아닌 type으로 선언</h3>
<pre><code class="language-tsx">export type SignupResDto = {
  id: string;
  name: string;
  email: string;
  phone: string;
  password: string;
};</code></pre>
<ul>
<li>dto를 클래스로 항상 선언해왔었음</li>
<li>type으로 export를 왜 해줬을까?</li>
<li></li>
</ul>
<p>TypeScript에서 <strong><code>type</code></strong>과 <strong><code>class</code></strong>를 사용하여 데이터 모델을 정의할 때, 각각의 선택이 가지는 이점과 상황에 따른 적합성을 고려하는 것이 중요합니다. 여기서 <strong><code>LoginReqDto</code></strong>를 <strong><code>type</code></strong>으로 정의한 경우의 이점을 살펴보겠습니다.</p>
<p><strong><code>class</code></strong>를 사용할 경우, 인스턴스 생성, 메소드 정의, 상속 등 더 복잡한 객체 지향 프로그래밍 기능이 필요할 때 적합합니다. 반면, <strong><code>type</code></strong>은 주로 데이터의 형태를 정의하는 데 초점을 맞추고 있습니다. 따라서, <strong><code>LoginReqDto</code></strong> 같은 간단한 데이터 전달 객체를 정의할 때는 <strong><code>type</code></strong>이 더 적합할 수 있습니다.</p>
<h3 id="indexts">index.ts</h3>
<p>각 폴더마다 index.ts 파일이 있었고, 각 파일들을 export 해주고 있었습니다.</p>
<p>무슨 의미인지 알아보았습니다.</p>
<p>모듈화와 재사용성을 위해 사용되는 패턴이며 간결한 임포트, 폴더 단위의 캡슐화 등의 장점이 있어 쓰인다고 합니다.</p>
<p>어떠한 파일을 import, export하는 걸 한번에 처리가 가능해 유지보수성도 높아집니다.</p>
<h3 id="injectentitymanager">@InjectEntityManager()</h3>
<ul>
<li>지금까지는 @InjectRepository만 써보았었습니다.</li>
<li>트랜잭션은 queryRunner를 사용해보았으나 EntityManager를 통해 하는 방법이 간결하고 한눈에 알아볼 수 있게 명확한 느낌을 받았습니다.</li>
<li><strong><code>EntityManager</code></strong>와 비교할 때, <strong><code>EntityManager</code></strong>는 주로 엔티티를 중심으로 데이터베이스 작업을 수행합니다.</li>
<li><strong><code>queryRunner</code></strong>는 복잡하고 세밀한 제어가 필요한 상황에서 유용하며, 특히 사용자가 트랜잭션을 수동으로 관리해야 할 때 적합합니다.</li>
</ul>
<pre><code class="language-tsx"> @InjectRepository(AccessToken)
    private readonly repo: Repository&lt;AccessToken&gt;,
    @InjectEntityManager()
    private readonly entityManager: EntityManager,
  ) {
    super(repo.target, repo.manager, repo.queryRunner);
  }</code></pre>
<ul>
<li><code>**repo.target**</code> → <strong><code>Repository</code></strong>가 작업하고 있는 엔티티 클래스를 가리킨다. <strong><code>AccessToken</code></strong> 클래스를 참조</li>
<li><strong><code>repo.manager</code> →</strong> 현재 <strong><code>Repository</code></strong>에 연결된 엔티티 매니저</li>
<li><code>**repo.queryRunner**</code> → 현재 <strong><code>Repository</code></strong>에서 사용 중인 <strong><code>QueryRunner</code></strong> 인스턴스를 가리킨다.</li>
<li></li>
</ul>
<h3 id="repository-pattern">Repository pattern</h3>
<ul>
<li>비즈니스 로직과 DB에 접근하는 로직을 구분</li>
<li>Repository는 entity객체와 함께 작동하며 개체 찾기, 삽입, 업데이트, 삭제 등을 처리</li>
<li>service 파일에서 비즈니스 로직을 처리할 때 <strong>DB작업은 Repository가 담당</strong>한다.</li>
</ul>
<p><strong><code>Promise&lt;void&gt;</code></strong> 반환 유형은 함수가 성공적으로 완료되면 특정 값이 아닌 완료 상태만 반환하고, 에러가 발생하면 예외를 던진다는 것</p>
<h3 id="주어진-문자열-형태의-만료시간을-계산-해당-시점을-나타내는-date-객체-반환-함수">주어진 문자열 형태의 만료시간을 계산, 해당 시점을 나타내는 Date 객체 반환 함수</h3>
<pre><code class="language-tsx">private calculateExpiry(expiry: string): Date {
    let expiresInMilliseconds = 0;

    if (expiry.endsWith(&#39;d&#39;)) {
      const days = parseInt(expiry.slice(0, -1), 10);
      expiresInMilliseconds = days * 24 * 60 * 60 * 1000;
    } else if (expiry.endsWith(&#39;h&#39;)) {
      const hours = parseInt(expiry.slice(0, -1), 10);
      expiresInMilliseconds = hours * 60 * 60 * 1000;
    } else if (expiry.endsWith(&#39;m&#39;)) {
      const minutes = parseInt(expiry.slice(0, -1), 10);
      expiresInMilliseconds = minutes * 60 * 1000;
    } else if (expiry.endsWith(&#39;s&#39;)) {
      const seconds = parseInt(expiry.slice(0, -1), 10);
      expiresInMilliseconds = seconds * 1000;
    } else {
      throw new HttpException(&#39;invalid expiry&#39;, HttpStatus.BAD_REQUEST);
    }

    return new Date(Date.now() + expiresInMilliseconds);
  }
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DevCamp - 3일차] 회원가입 API 개발]]></title>
            <link>https://velog.io/@choisooyoung-dev/DevCamp-3%EC%9D%BC%EC%B0%A8-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85-API-%EA%B0%9C%EB%B0%9C</link>
            <guid>https://velog.io/@choisooyoung-dev/DevCamp-3%EC%9D%BC%EC%B0%A8-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85-API-%EA%B0%9C%EB%B0%9C</guid>
            <pubDate>Wed, 20 Mar 2024 19:36:41 GMT</pubDate>
            <description><![CDATA[<p>postgresql</p>
<ul>
<li>설치부터 시행착오가 많았다.(환경변수 문제, 사용자 권한 문제 등)</li>
<li>mysql과 다르지 않았지만 왜 쓰는지?</li>
<li>병렬 쿼리 처리 기능을 지원해 대용량 데이터를 처리할 때 여러 CPU 코어를 활용하여 더 빠르게 데이터를 처리하는 데 도움을 준다고 한다.</li>
<li>대용량 트래픽 처리 프로젝트에 쓰인다.</li>
</ul>
<p>repository 파일</p>
<ul>
<li>한번도 만들어보지 않았던 파일이었고, 왜 따로 만들어야하는지에 대한 의문이 생겼다.</li>
<li>각 부분이 단일 책임 원칙을 더 잘 준수할 수 있도록 데이터 로직과 비즈니스 로직을 분리하여 작업하는 것이 좋다고 한다.</li>
</ul>
<p>파일 분리</p>
<ul>
<li>예시코드 전반적으로 각 파일들의 역할이 확실하게 나누어져 있는 느낌을 받았다.</li>
<li>오늘 제출하는 과제에서 최대한 적용해보았다.</li>
</ul>
<p>base-entity</p>
<ul>
<li>공통적인 엔티티 구성요소를 따로 분리해서 가져다 쓰니 정말 편리했다.</li>
</ul>
<blockquote>
<p>어제와 달리 직접 예시코드를 참고하면서 회원가입 API 구현해보니 항상 무언가를 구현하기 전 어떤 방식으로 파일들을 구성해야할지에 대해 고민했던 것에 대해 조금이나마 해소가 되었던 경험이었다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DevCamp - 2일차] 예시 코드 뜯어보기]]></title>
            <link>https://velog.io/@choisooyoung-dev/DevCamp-2%EC%9D%BC%EC%B0%A8-%EC%98%88%EC%8B%9C-%EC%BD%94%EB%93%9C-%EB%9C%AF%EC%96%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@choisooyoung-dev/DevCamp-2%EC%9D%BC%EC%B0%A8-%EC%98%88%EC%8B%9C-%EC%BD%94%EB%93%9C-%EB%9C%AF%EC%96%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Tue, 19 Mar 2024 15:45:32 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>예시로 받은 코드를 뜯어보고 전에 구현했던 내 방식에서 생각하지 못한 부분들 위주로 작성해보았다.</p>
</blockquote>
<h3 id="jwt-블랙리스트">JWT 블랙리스트</h3>
<ul>
<li>단순히 잘못된 토큰을 가진 정보들을 모아둔 것이라 생각했다. </li>
<li>한번 발급한 jwt를 무효화할 방법이 없으니 로그아웃 해도 만료시간 전에 해당 토큰을 그대로 사용할 수 있는 문제를 해결하기 위한 방법</li>
<li>지난 프로젝트에서는 따로 생각해보지 못한 부분</li>
</ul>
<h3 id="로그인시-접속-로그-저장의-이유">로그인시 접속 로그 저장의 이유?</h3>
<ul>
<li>지금까지 프로젝트를 하면서 간과했던 부분</li>
<li>사용자의 접속 로그를 모니터링하면 비정상적인 접근 시도나 패턴을 식별하여 보안 위협을 초기에 알 수 있으며, 시스템의 오류나 문제를 진단하는 데 도움을 주고, 사용자의 행동을 분석하여 사용자 경험을 개선하고, 맞춤형 콘텐츠나 광고 제공에도 쓰임
또한 비즈니스 결정의 유용한 인사이트 제공하여 그에 맞는 전략 수립시에도 쓰임</li>
<li>request 객체에서 받을 수 있는 ip, endpoint, user agent를 활용하신걸로 보임</li>
<li>endpoint? -&gt;  사용자가 어떤 작업을 수행하려고 했는지, 어떤 페이지나 리소스에 관심이 있는지</li>
<li>user agent? -&gt; 사용자가 요청을 보낼 때 사용한 브라우저, 운영 체제, 장치 등에 대한 정보</li>
</ul>
<h3 id="argon2">argon2</h3>
<ul>
<li>기존 비밀번호 해싱은 crypto나 bcrypt를 사용해왔음</li>
<li>argon2 는 암호를 해싱하는데 걸리는 시간이나 소요되는 메모리 양을 설정할 수 있게 설계되었고, 사용하는 목적에 맞게 파라미터 변경으로 적용이 가능</li>
<li>키 유도 함수(key derivation function)</li>
<li>Argon2d, Argon2i, Argon2id 3종류이며 비밀번호 해싱에 적합한 것은 Argon2id</li>
</ul>
<p>해싱처리</p>
<pre><code class="language-ts">const hashedPassword = await argon2.hash(dto.password);</code></pre>
<p>인증처리</p>
<pre><code class="language-ts"> if (user &amp;&amp; (await argon2.verify(user.password, plainPassword))) {
      return user;
    }</code></pre>
<h3 id="키-유도-함수">키 유도 함수?</h3>
<ul>
<li>일정한 길이의 비밀 키를 생성하기 위해 비밀 비트 문자열(예: 비밀번호, 패스프레이즈)을 사용하는 암호학적 함수</li>
<li>여러 다른 키도 생성 가능하며 복호화도 가능</li>
</ul>
<h3 id="메모리-사용량-연산-시간-병렬-처리-등-다양한-파라미터-조정과-보안이-어떠한-연관성이-있는지">메모리 사용량, 연산 시간, 병렬 처리 등 다양한 파라미터 조정과 보안이 어떠한 연관성이 있는지?</h3>
<ul>
<li>메모리 사용량을 높이면 공격자가 동시에 많은 수의 해시를 계산하는 것이 더 어려워진다고 합니다.</li>
<li><blockquote>
<p>대규모 병렬처리 이용 공격(GPU, ASIC 등) 저항력 강화</p>
</blockquote>
</li>
<li>연산 시간을 늘리면 해시를 계산하는 데 더 많은 시간이 소요</li>
<li><blockquote>
<p>무차별 대입 공격에 대한 저항력 강화</p>
</blockquote>
</li>
<li>병렬 처리는 해시 계산 시 사용할 수 있는 병렬 쓰레드 수를 지정함으로써 수준을 높이면 해싱 수행시 많은 CPU 자원이 필요</li>
<li><blockquote>
<p>비용 대비 효과적인 공격에 대한 어려움이 증가, 시간-메모리-트레이드오프(TMT, 해싱 과정에서 메모리와 시간 사이의 관계를 이용하는 공격) 공격에 대한 저항력 강화 </p>
</blockquote>
</li>
</ul>
<h2 id="처음-본-기술스택">처음 본 기술스택</h2>
<ol>
<li>SWC(Speedy Web Compiler)</li>
</ol>
<ul>
<li>기존에 알던 babel의 역할과 동일해보였으나 babel의 싱글 쓰레드의 속도 문제를 해결한 컴파일러로 작게는 몇 배, 크게는 몇 십 배나 빠른 성능을 자랑하는 빌드 도구</li>
<li>swc 사용의 이유를 정리하자면, 빠른 속도로 자바스크립트 코드를 트랜스파일 하거나 타입스크립트 컴파일을 위해</li>
</ul>
<ol start="2">
<li>Yarn berry + Plug&#39;n&#39;Play + Zero-Install</li>
</ol>
<ul>
<li>각각의 프로그램인줄 알았으나 Plug&#39;n&#39;Play와 Zero-Install은 Yarn berry의 중요한 특징들이었음</li>
<li>Plug&#39;n&#39;Play (PnP)는 기존의 node_modules 폴더 방식 대신, PnP 방식을 도입하여 의존성을 관리</li>
<li><blockquote>
<p>프로젝트의 부팅 시간 단축시키는 효과와 파일 시스템을 더 효율적으로 사용하게 해준다.</p>
</blockquote>
</li>
<li>Zero-Installs의 개념은 프로젝트의 의존성 정보를 .yarn/cache에 저장하여, 다른 개발자와의 협업 시 의존성 설치 과정을 건너뛰게 해주는 것</li>
<li><blockquote>
<p>CI/CD 파이프라인에서의 빌드시간 단축의 효과가 있다.</p>
</blockquote>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DevCamp - 1일차] 로그인, 회원가입에 필요한 Nest.js, TypeORM 기능들 알아보기]]></title>
            <link>https://velog.io/@choisooyoung-dev/Devcamp-1%EC%9D%BC%EC%B0%A8-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85%EC%97%90-%ED%95%84%EC%9A%94%ED%95%9C-Nest.js-TypeORM-%EA%B8%B0%EB%8A%A5%EB%93%A4-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@choisooyoung-dev/Devcamp-1%EC%9D%BC%EC%B0%A8-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85%EC%97%90-%ED%95%84%EC%9A%94%ED%95%9C-Nest.js-TypeORM-%EA%B8%B0%EB%8A%A5%EB%93%A4-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Mon, 18 Mar 2024 10:41:04 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>데브캠프 1일차의 기록
데브캠프는 현업 수준의 코드를 직접 살펴보며 스스로 그에 준하는 수준의 개발을 할 수 있다.
최종프로젝트 이후로 nest.js는 거의 놓은 상태인 나에게 아주 좋은 기회였다.
이번엔 빠르게 만들어서 기능만 되면 완료해버리는것이 아닌 좋은 코드의 예시를 뜯어보며 나의 수준도 같이 높이는게 목표이다. 기록도 틈틈이 하기!</p>
</blockquote>
<h1 id="✅-기본적으로-알아야-할-nestjs-개념-리마인드">✅ 기본적으로 알아야 할 Nest.js 개념 리마인드</h1>
<h3 id="controller">Controller</h3>
<ul>
<li>클라이언트측에서 오는 HTTP request 받아주는 역할
<img src="https://velog.velcdn.com/images/choisooyoung-dev/post/1c91b3ed-5bb8-4f89-8cb7-4bed90beabab/image.png" alt=""></li>
</ul>
<h3 id="routing">Routing</h3>
<ul>
<li>기본적인 Get, Post, Put, Delete 데코레이터 제공</li>
<li>@Param, @Body로 요청 객체의 정보 읽기</li>
<li>DTO로 입출력 정의</li>
</ul>
<h3 id="provider">Provider</h3>
<ul>
<li>의존성 주입의 대상들</li>
</ul>
<h3 id="의존성-주입">의존성 주입</h3>
<pre><code>@Get()
findAll(@Req() request: Request) {
  return this.sampleService.findAll();
}</code></pre><ul>
<li>sampleService의 findAll을 바로 저 자리에서 구현하는 것이 아닌 어딘가에 구현되어 있다고 생각하고 사용</li>
<li>-&gt; sampleService에 의존하고 있다</li>
<li>의존하며 쓰려면 의존성 주입을 해줘야 한다.</li>
</ul>
<h3 id="의존성-주입-방법">의존성 주입 방법</h3>
<ol>
<li>@Injectable → Provider 역할 부여</li>
<li>Module에서 @Inject 사용하여 명세해주기</li>
</ol>
<h3 id="module">Module</h3>
<ul>
<li>큼직하게 묶인 것들
<img src="https://velog.velcdn.com/images/choisooyoung-dev/post/7575438e-172d-43d8-b003-7aeea8452ba9/image.png" alt=""></li>
</ul>
<p>imports -&gt;    갖다 쓸 모듈
controllers -&gt;    외부 노출할거
providers -&gt;    의존성 주입할거
exports -&gt;    다른 애들이 쓸거</p>
<h3 id="⚠-global-module">⚠ Global Module</h3>
<ul>
<li>모듈의 명세를 깨뜨리므로 제한적으로 사용 권장</li>
<li>imports 없이 마구 사용할 수 있다</li>
</ul>
<h3 id="pipe">Pipe</h3>
<p><img src="https://velog.velcdn.com/images/choisooyoung-dev/post/cbe5c0a3-200f-496b-a5f4-033e083150ee/image.png" alt=""></p>
<ul>
<li>Input을 원하는 형태로 바꾸거나 타당성을 검증해준다.</li>
<li>Transform, Validation</li>
<li>Schema-based validation도 가능하다</li>
<li>DTO 포맷으로 입출력 검증하고 싶다면? → class-validator 사용</li>
<li>내장 파이프도 있고, 커스터마이징도 가능하다.</li>
</ul>
<hr>
<h1 id="✅typeorm">✅TypeORM</h1>
<ul>
<li>Node.js 환경에서 사용되는 객체 관계 매핑 라이브러리</li>
</ul>
<h3 id="객체-관계-매핑orm">객체 관계 매핑(ORM)</h3>
<ul>
<li>객체 지향 프로그래밍 언어를 사용하여 호환되지 않는 유형의 시스템 간에 데이터를 변환하는 프로그래밍 기술</li>
</ul>
<h3 id="객체-관계-매핑orm-이점">객체 관계 매핑(ORM) 이점</h3>
<ol>
<li>생산성 향상</li>
</ol>
<ul>
<li>SQL쿼리 작성 대신 js코드로 작성 및 관리 가능</li>
</ul>
<ol start="2">
<li>유지보수성 향상</li>
</ol>
<ul>
<li>데이터베이스 구조 변경되더라도 애플리케이션 자체를 크게 수정하지 않아도 됨</li>
</ul>
<ol start="3">
<li>플랫폼 독립성</li>
</ol>
<ul>
<li>데이터베이스 세부 사항을 추상화 -&gt; 특정 데이터베이스 시스템에 종속되지 않게 된다.</li>
<li>동일한 코드 베이스를 다양한 데이터베이스 시스템에 적용할 수 있다.</li>
</ul>
<ol start="4">
<li>보안강화</li>
</ol>
<ul>
<li>SQL Injection 공격과 같은 보안 취약점 줄일 수 있다.</li>
<li>쿼리가 파라미터화되어 실행되므로 악의적인 코드 삽입하는 것이 더 어렵게 된다.</li>
</ul>
<hr>
<h1 id="✅-로그인-회원가입에-필요한-정보-정리예시코드">✅ 로그인, 회원가입에 필요한 정보 정리(+예시코드)</h1>
<ol>
<li><p>TypeORM 설정 -&gt; @nestjs/typeorm typeorm mysql2</p>
</li>
<li><p>엔티티 생성</p>
<pre><code class="language-ts">@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;

@Column()
firstName: string;

@Column()
lastName: string;

@Column({ default: true })
isActive: boolean;
}</code></pre>
</li>
<li><p>인증 </p>
<pre><code class="language-ts">async signIn(username: string, pass: string): Promise&lt;any&gt; {
 const user = await this.usersService.findOne(username);
 if (user?.password !== pass) {
   throw new UnauthorizedException();
 }
 const { password, ...result } = user;
 // TODO: Generate a JWT and return it here
 // instead of the user object
 return result;
}</code></pre>
</li>
<li><p>인증 검증</p>
</li>
<li><p>예외처리 -&gt; 내장 HttpException(@nestjs/common)</p>
</li>
<li><p>JWT 토큰 -&gt; @nestjs/jwt</p>
</li>
<li><p>비번 해싱처리</p>
</li>
<li><p>API 명세</p>
</li>
<li><p>가드(경로에 따른) - 로그인한 유저만 허용되는 경로</p>
<pre><code class="language-ts">import { Injectable, CanActivate, ExecutionContext } from &#39;@nestjs/common&#39;;
import { Observable } from &#39;rxjs&#39;;
</code></pre>
</li>
</ol>
<p>@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    return validateRequest(request);
  }
}</p>
<pre><code>10. Validation -&gt; class-validator class-transformer</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] NginX]]></title>
            <link>https://velog.io/@choisooyoung-dev/TIL-NginX</link>
            <guid>https://velog.io/@choisooyoung-dev/TIL-NginX</guid>
            <pubDate>Thu, 15 Feb 2024 18:51:42 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Nginx를 이해하기 위해서는 왜 nginx가 탄생하게 되었는지 전체적인 흐름에 대해서 파악해봐야 한다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/choisooyoung-dev/post/1bdba842-11c3-465e-8d31-f0da04d44183/image.png" alt=""></p>
<h1 id="아파치-탄생-1995">아파치 탄생 1995</h1>
<p>아파치는 요청이 생길때마다 연결할 때 process를 하나씩 만들어 낸다.
process 자체를 만드는 시간은 오래 걸리니까 미리 만들어 놓는 prefork 방식을 사용했다.
그래서 새로운 요청이 들어오게되면 미리 만들어 놓은 process를 바로 사용하였다.
만들어 놓은 process가 모두 할당되면 추가로 process를 만들어 놓는 방식이었다.</p>
<p>이러한 구조는 다양한 모듈을 만들어서 서버에 빠르게 기능을 추가할 수 있게 해줬고, 아파치 서버는 동적 컨텐츠를 처리할 수도 있게 되었다.
확장성이 좋다는 장점도 갖고 있어 덕분에 요청을 받고 응답을 처리하는 과정을 하나의 서버에서 해결할 수 있게 되었다.</p>
<h1 id="c10k-문제-1999">C10K 문제 1999</h1>
<p>인터넷 트래픽이 계속해서 증가되던 시기.
이때 서버에 동시에 연결되는 커넥션이 많아졌을 때 더이상 커넥션을 형성하지 못하는 문제가 생기게 되었다.</p>
<p>이러한 문제를 바로 C10K, 커넥션 10,000개의 문제라고 한다.</p>
<blockquote>
<p>동시 연결된 커넥션 수 - 요청을 처리하기 위해 서버가 한 시점에 얼마나 많은 클라이언트와 커넥션을 형성하고 있는 지
초당 요청 처리 수 - 초당 서버가 얼마나 빨리 요청을 처리할 수 있는 지</p>
</blockquote>
<h1 id="새로운-구조-채택-2004">새로운 구조 채택 2004</h1>
<h1 id="스마트-폰-탄생-2008">스마트 폰 탄생 2008</h1>
<h1 id="apach-mpms-vs-nginx">Apach MPMs vs Nginx</h1>
<p><img src="https://velog.velcdn.com/images/choisooyoung-dev/post/9f8da085-fa57-46e4-83d8-cdd3d8f94580/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/choisooyoung-dev/post/4ef9e476-c0fb-40d3-b651-873e1780a492/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 쿠버네티스]]></title>
            <link>https://velog.io/@choisooyoung-dev/TIL-%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4</link>
            <guid>https://velog.io/@choisooyoung-dev/TIL-%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4</guid>
            <pubDate>Thu, 15 Feb 2024 00:15:06 GMT</pubDate>
            <description><![CDATA[<p>쿠버네티스
<img src="https://velog.velcdn.com/images/choisooyoung-dev/post/b570d02b-0a7e-44b0-9319-238a09829ff7/image.png" alt="">
<img src="https://velog.velcdn.com/images/choisooyoung-dev/post/3e9baf84-852c-452d-a4fe-c3bdf484e06c/image.png" alt=""></p>
<p>클라우드 네이티브</p>
<p><img src="https://velog.velcdn.com/images/choisooyoung-dev/post/f0df3ab8-bf50-4f6d-817b-3a6f089b631c/image.png" alt=""></p>
<p>클라우드 네이티브 애플리케이션의 방법론은 데브옵스(DevOps), 애플리케이션 운영 구조는 마이크로서비스, 애플리케이션 운영 인프라는 컨테이너로 이야기할 수 있습니다. 지속적인 통합/배포(CI/CD)는 애플리케이션을 더욱 짧은 주기로 고객에게 제공하는 자동화 프로세스</p>
<p>컨테이너
컨테이너는 환경과 상관없이 실행을 위해 필요한 모든 요소를 포함하는 소프트웨어 패키지입니다. 컨테이너라는 분리된 공간에 애플리케이션과 운영 환경이 모두 들어있고, 그 덕에 분리된 공간마다 다른 환경을 제공할 수 있죠. 마치 하나하나가 별도의 서버인 것처럼 사용할 수 있게 만든 기술</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 부하테스트]]></title>
            <link>https://velog.io/@choisooyoung-dev/TIL-%EB%B6%80%ED%95%98%ED%85%8C%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@choisooyoung-dev/TIL-%EB%B6%80%ED%95%98%ED%85%8C%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Tue, 13 Feb 2024 21:00:03 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.artillery.io/docs/get-started/get-artillery">https://www.artillery.io/docs/get-started/get-artillery</a>
<a href="https://inpa.tistory.com/entry/JEST-%F0%9F%93%9A-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-Stress-Test">https://inpa.tistory.com/entry/JEST-%F0%9F%93%9A-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-Stress-Test</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 서버가 뭘까? (Nginx, Apach..)]]></title>
            <link>https://velog.io/@choisooyoung-dev/TIL-%EC%84%9C%EB%B2%84%EA%B0%80-%EB%AD%98%EA%B9%8C-Nginx-Apach</link>
            <guid>https://velog.io/@choisooyoung-dev/TIL-%EC%84%9C%EB%B2%84%EA%B0%80-%EB%AD%98%EA%B9%8C-Nginx-Apach</guid>
            <pubDate>Fri, 09 Feb 2024 19:42:23 GMT</pubDate>
            <description><![CDATA[<p>꾸준히 듣고 있는 용어들중 하나였던 서버, 막상 서버가 뭐냐고 하면 </p>
<p><img src="https://velog.velcdn.com/images/choisooyoung-dev/post/5b3a8042-b4bc-4d55-813e-ce416106d854/image.png" alt=""></p>
<p>그래서 정리해보려 한다 😁</p>
<br />
<br />

<h1 id="도대체-서버가-뭘까">도대체 서버가 뭘까?</h1>
<p>서버란, 클라이언트 요청에 응답을 해주는 것.</p>
<p>전체적으로 클라이언트가 요청에 대한 응답을 받는 로직을 생각해보자.</p>
<p>식당에서는!
손님이 주문을 하면 홀 서빙 담당이 주문을 받아 주방쪽에 주문을 전달한다.
이때 주방장은 손님이 주문한 주문대로 레시피에 써있는 소분된 재료를 냉장고에서 가져온다.
재료를 갖고 온 주방장은 주방장이 원하는 방식으로 조리한 후 홀 서버에게 메뉴를 전달하고,
해당 메뉴를 주문한 손님에게 전달하는 방식이다.</p>
<p><img src="https://velog.velcdn.com/images/choisooyoung-dev/post/921ecd11-e697-41f7-98d4-43bd5abdb538/image.png" alt=""></p>
<p>로직으로 표현하자면,</p>
<p>손님
<code>클라이언트</code></p>
<p>홀 담당
<code>서버</code></p>
<p>주방장
<code>백엔드 언어</code></p>
<p>냉장고
<code>데이터베이스</code></p>
<p><img src="https://velog.velcdn.com/images/choisooyoung-dev/post/25d92264-c493-49ac-9e6d-adf29879c911/image.png" alt=""></p>
<h3 id="따라서-서버는-클라이언트-요청을-받아-백엔드에-전달해주고-요청에-따른-응답을-해주는-역할을-한다">따라서 서버는 클라이언트 요청을 받아 백엔드에 전달해주고, 요청에 따른 응답을 해주는 역할을 한다.</h3>
<br />
<br />

<h2 id="번외-프로토콜과-포트">번외) 프로토콜과 포트</h2>
<p>클라이언트는 해당 가게를 찾아가서 주문하려면 가게의 주소, 들어갈 때의 문(손님용 문, 직원용 문이 있는 가게라면), 주문방식을 알아야 한다.</p>
<p>다시 로직으로 표현하자면,</p>
<p>가게 위치(2가지 방식)</p>
<ol>
<li><code>IP</code><ul>
<li>192.120.000.000</li>
</ul>
</li>
<li><code>도메인</code><ul>
<li><a href="https://velog.io/@choisooyoung-dev">https://velog.io/@choisooyoung-dev</a></li>
</ul>
</li>
</ol>
<p>들어오는 문
<code>PORT</code></p>
<p>주문방식
<code>Protocol</code></p>
<h3 id="프로토콜과-포트">프로토콜과 포트</h3>
<p>두가지 관점으로 나눌 수 있는데 해당 포트 번호는 관례적으로 쓰이는 번호들이다.</p>
<ul>
<li><p>일반 클라이언트들이 쓰는 프로토콜과 포트번호
HTTP -&gt; 80, HTTPS -&gt; 443</p>
</li>
<li><p>관리자들이 쓰는 프로토콜과 포트번호
SSH -&gt; 22, MySQL -&gt; 3306 등</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 실시간 스트리밍 딜레이 문제 - NginX worker_processes과의 연관성 테스트]]></title>
            <link>https://velog.io/@choisooyoung-dev/TIL-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D-%EB%94%9C%EB%A0%88%EC%9D%B4-%EB%AC%B8%EC%A0%9C-NginX-workerprocesses%EA%B3%BC%EC%9D%98-%EC%97%B0%EA%B4%80%EC%84%B1</link>
            <guid>https://velog.io/@choisooyoung-dev/TIL-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D-%EB%94%9C%EB%A0%88%EC%9D%B4-%EB%AC%B8%EC%A0%9C-NginX-workerprocesses%EA%B3%BC%EC%9D%98-%EC%97%B0%EA%B4%80%EC%84%B1</guid>
            <pubDate>Wed, 07 Feb 2024 16:33:35 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/choisooyoung-dev/post/17c24a5d-8ea2-4dda-8442-6a94cb720e9c/image.png" alt=""></p>
<p>프로젝트 nginx config 파일에 최상단에 위치한 <code>worker_processes</code></p>
<h2 id="worker_processes가-뭔지">worker_processes가 뭔지?</h2>
<p><img src="https://velog.velcdn.com/images/choisooyoung-dev/post/0d01a304-f49b-4d1c-a0e4-9ad9d47c834c/image.png" alt=""></p>
<ul>
<li>Nginx가 요청을 처리하기 위해 생성할 수 있는 프로세스의 수를 지정하는 설정</li>
<li>Master 서버가 있으면 Worker process에게 작업을 할당해준다.</li>
<li>Master는 아무것도 하지 않고 Worker process들이 다 한다.</li>
<li>Worker process는 많을수록 요청에 대한 처리가 효율적이다.</li>
<li>worker_process는 독립적으로 실행되며, CPU 코어와 연관될 수 있어, 멀티코어 시스템에서 효율적으로 작동한다.</li>
</ul>
<h3 id="요청에-대한-처리부터-효율적으로-받아오면-딜레이는-단축될-것인가에-대한-실험을-팀원들이랑-해보았다">요청에 대한 처리부터 효율적으로 받아오면 딜레이는 단축될 것인가에 대한 실험을 팀원들이랑 해보았다.</h3>
<ul>
<li>단순히 worker_processes 할당 값을 올려주는 방식대로 진행해보았다.</li>
<li>값마다 두번씩 측정해줬고, 혹시 몰라서 운영체제 별로 실험을 진행해보았다(Mac, Windows)</li>
</ul>
<br />
<br />


<h3 id="mac에서의-결과값-cpu-사용량--딜레이-차이-순서로-기재">Mac에서의 결과값 (CPU 사용량 / 딜레이 차이 순서로 기재)</h3>
<table>
<thead>
<tr>
<th>Worker_process</th>
<th>1번째 측정</th>
<th>두번째 측정</th>
</tr>
</thead>
<tbody><tr>
<td>auto</td>
<td>13.32% / 14.013ms</td>
<td>15.65% / 33.149ms</td>
</tr>
<tr>
<td>1</td>
<td>3.81% / 14.013ms</td>
<td>2.83% / 13.371ms</td>
</tr>
<tr>
<td>2</td>
<td>5.95% / 15.802ms</td>
<td>6.67% / 29.374ms</td>
</tr>
<tr>
<td>4</td>
<td>5.51% / 13.761ms</td>
<td>6.57% / 31.296ms</td>
</tr>
<tr>
<td>6</td>
<td>11.24% / 12.542ms</td>
<td>9.51% / 13.371ms</td>
</tr>
<tr>
<td>8</td>
<td>19.7% / 12.166ms</td>
<td>11.24% / 12.988ms</td>
</tr>
<tr>
<td>10</td>
<td>16.07% / 12.988ms</td>
<td>19.64% / 31.867ms</td>
</tr>
<tr>
<td>16</td>
<td>27.88% / 23.230ms</td>
<td>27.15% / 12.037ms</td>
</tr>
</tbody></table>
<br />

<h3 id="windows에서의-결과값-cpu-사용량--딜레이-차이-순서로-기재">Windows에서의 결과값 (CPU 사용량 / 딜레이 차이 순서로 기재)</h3>
<table>
<thead>
<tr>
<th>Worker_process</th>
<th>1번째 측정</th>
<th>두번째 측정</th>
</tr>
</thead>
<tbody><tr>
<td>auto</td>
<td>20.94% / 19.711ms</td>
<td>6.33% / 30.783ms</td>
</tr>
<tr>
<td>1</td>
<td>1.86% / 16.638ms</td>
<td>2.24% / 15.296ms</td>
</tr>
<tr>
<td>2</td>
<td>2.44% / 12.099ms</td>
<td>2.25% / 28.542ms</td>
</tr>
<tr>
<td>4</td>
<td>2.66% / 13.177ms</td>
<td>3.06% / 33.035ms</td>
</tr>
<tr>
<td>6</td>
<td>2.85% / 13.051ms</td>
<td>20.12% / 11.710ms</td>
</tr>
<tr>
<td>8</td>
<td>19.16% / 11.263ms</td>
<td>20.34% / 12.166ms</td>
</tr>
<tr>
<td>10</td>
<td>8.29% / 12.477ms</td>
<td>5.12% / 13.696ms</td>
</tr>
<tr>
<td>16</td>
<td>38.75% / 12.925ms</td>
<td>7.69% / 09.983ms</td>
</tr>
</tbody></table>
<br />
<br />

<h2 id="결과">결과</h2>
<h3 id="mac">Mac</h3>
<ul>
<li><code>worker_processes: 6</code> 설정이 비교적 낮은 CPU 사용량(11.24%, 9.51%)과 딜레이(12.542ms, 13.371ms)를 제공하여, Mac 환경에서 가장 효율적인 균형을 보인다.</li>
</ul>
<h3 id="windows">Windows</h3>
<ul>
<li><code>worker_processes: 6</code> 두 번째 측정에서 매우 낮은 딜레이(11.710ms)와 높은 CPU 사용량(20.12%)을 보여주는데, 이는 특정 상황에서는 우수한 성능을 나타낼 수 있음 </li>
<li>그러나 균형성이 좋은건 <code>worker_processes: 2</code></li>
</ul>
<br />
<br />


<p>물론 유저마다 운영체제도 다르고 스펙도 다르기 때문에 정확한 수치라고는 보기 어렵다.
또한 worker_process가 딜레이에 영향을 주는지, 요청에 관한 처리가 효율적으로 진행될 때 딜레이도 같이 영향을 주는 지에 대한 결과는 정확히 나타낼 수 없고, 영상 자체에 처리부분을 효율적으로 처리하는 것이 좀 더 좋은 결과를 줬다는 것을 알게되었다.</p>
<p>나름 이것 저것 팀원들이랑 약간의 노가다성인 테스트였지만 흥미로웠고, 여러 상황을 다 테스트 할 수 없어 결론을 바로 짓기 어려운 상황이었기 때문에 결과가 도출되지 않은걸까 싶기도 하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 실시간 스트리밍 지연 문제 - 트랜스코딩 설정 조정 결과]]></title>
            <link>https://velog.io/@choisooyoung-dev/TIL-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D-%EC%A7%80%EC%97%B0-%EB%AC%B8%EC%A0%9C-%ED%8A%B8%EB%9E%9C%EC%8A%A4%EC%BD%94%EB%94%A9-%EC%84%A4%EC%A0%95-%EC%A1%B0%EC%A0%95-%EA%B2%B0%EA%B3%BC</link>
            <guid>https://velog.io/@choisooyoung-dev/TIL-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D-%EC%A7%80%EC%97%B0-%EB%AC%B8%EC%A0%9C-%ED%8A%B8%EB%9E%9C%EC%8A%A4%EC%BD%94%EB%94%A9-%EC%84%A4%EC%A0%95-%EC%A1%B0%EC%A0%95-%EA%B2%B0%EA%B3%BC</guid>
            <pubDate>Tue, 06 Feb 2024 18:56:01 GMT</pubDate>
            <description><![CDATA[<h1 id="동일한-조건">동일한 조건</h1>
<ul>
<li>로컬 환경에서 테스트</li>
<li>OBS Studio 세팅 값
<img src="https://velog.velcdn.com/images/choisooyoung-dev/post/984ead44-a7b6-4c36-98bb-3262c412c367/image.png" alt=""></li>
<li>서버 키자마자 바로 첫번째로 측정!
한 결과 값<ul>
<li>여러번 할수록 딜레이 시간이 늘어나고 있는 상황</li>
</ul>
</li>
</ul>
<br />

<h1 id="세팅">세팅</h1>
<pre><code class="language-bash">...
rtmp {
    server {
        listen 1935;
        listen [::]:1935 ipv6only=on;    
         application stream {
            live on;

            exec ffmpeg -i rtmp://localhost:1935/stream/$name
              -c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 750k -f flv -g 30 -r 30 -s 640x360 -preset superfast -profile:v baseline rtmp://localhost:1935/live/$name_360p878kbs
              -c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 400k -f flv -g 30 -r 30 -s 426x240 -preset superfast -profile:v baseline rtmp://localhost:1935/live/$name_240p528kbs
              -c:a libfdk_aac -b:a 64k -c:v libx264 -b:v 200k -f flv -g 15 -r 15 -s 426x240 -preset superfast -profile:v baseline rtmp://localhost:1935/live/$name_240p264kbs;

...</code></pre>
<br />
<br />

<h2 id="프리셋">프리셋</h2>
<pre><code class="language-bash">-preset superfast</code></pre>
<ul>
<li>빠른 인코딩 속도를 제공</li>
<li>ultrafast로 변경하면 인코딩 속도를 더욱 높일 수 있다.</li>
<li>딜레이를 줄이는 데 도움이 될 수 있지만, 비트레이트 대비 품질이 다소 저하될 수 있다.</li>
</ul>
<br />

<h2 id="프리셋-조정-superfast---ultrafast">프리셋 조정 superfast -&gt; ultrafast</h2>
<pre><code class="language-bash">-preset ultrafast</code></pre>
<br />
<br />

<h2 id="키-프레임-간격">키 프레임 간격</h2>
<pre><code class="language-bash">flv -g 30</code></pre>
<ul>
<li>키 프레임 간격(-g 옵션)은 스트리밍의 지연 시간에 영향을 줄 수 있다.</li>
<li>딜레이와 품질의 균형을 맞춰야 하기 때문에 키 프레임 간격을 3초에서 5초 사이로 설정해야 한다.</li>
</ul>
<br />

<h2 id="키-프레임-간격-조정-30---90-15---90">키 프레임 간격 조정 30 -&gt; 90, 15 -&gt; 90</h2>
<ul>
<li>기존 1초당 30프레임</li>
<li>조정 값 3초</li>
</ul>
<pre><code class="language-bash"> exec ffmpeg -i rtmp://localhost:1935/stream/$name
              -c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 750k -f flv -g 90 -r 30 -s 640x360 -preset ultrafast -profile:v baseline rtmp://localhost:1935/live/$name_360p878kbs
              -c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 400k -f flv -g 90 -r 30 -s 426x240 -preset ultrafast -profile:v baseline rtmp://localhost:1935/live/$name_240p528kbs
              -c:a libfdk_aac -b:a 64k -c:v libx264 -b:v 200k -f flv -g 90 -r 15 -s 426x240 -preset ultrafast -profile:v baseline rtmp://localhost:1935/live/$name_240p264kbs;</code></pre>
<br />
<br />

<h1 id="결과">결과</h1>
<br />

<h2 id="기존-세팅-딜레이-20초">기존 세팅, 딜레이 20초</h2>
<p><img src="https://velog.velcdn.com/images/choisooyoung-dev/post/bdabf967-79b1-4ada-9414-32a1fc971e38/image.png" alt=""></p>
<br />

<h2 id="조정-후-딜레이-10초">조정 후, 딜레이 10초</h2>
<p><img src="https://velog.velcdn.com/images/choisooyoung-dev/post/39ecb027-62dc-448b-bc63-fc5a30b6e492/image.png" alt=""></p>
<br />
<br />

<p>로컬 환경에서 테스트 해봤을때  딜레이에 대한 문제는 트랜스코딩만으로는 해결되지는 않지만 꽤 의미있는 결과가 나왔다.</p>
<p>그러나 도커 서버를 실행하자마자 바로 방송을 켜서 딜레이 측정했을때에 비해 좀 더 켜놓고(시간이 지난 후) 방송도 여러번 켜고 나서 실행 됐을때의 딜레이가 점점 더 늘어나고 있다.</p>
<ul>
<li>처음 켰을때 조정 전 20초, 조정 후 10초</li>
<li>방종 후 또 켰을때(시간이 좀 지체된 후) 조정 전 44초, 조정 후 27초</li>
</ul>
<p>서버의 문제일수도 있지만 정확히 왜그런지에 대해 알아봐야겠다.
그치만 딜레이 차이 부분에서는 꼭 각 서버에서 첫번째 방송이 아니여도 조정 전, 후 딜레이 차이는 비슷하게 나고 있는 상황으로 보아 의미 있는 테스트였다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 실시간 스트리밍 지연 문제]]></title>
            <link>https://velog.io/@choisooyoung-dev/TIL-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D-%EC%A7%80%EC%97%B0-%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@choisooyoung-dev/TIL-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D-%EC%A7%80%EC%97%B0-%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Tue, 06 Feb 2024 08:45:28 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>실시간 스트리밍 방송 서비스 프로젝트를 진행중인데 OBS에서 송출되는 영상과 보여지고 있는 영상의 딜레이가 너무 길었다 </p>
</blockquote>
<p>배포 전 -&gt; 17초 이상
배포 후 -&gt; 24 ~ 30초 이상</p>
<h1 id="딜레이의-원인">딜레이의 원인</h1>
<ol>
<li>네트워크 지연</li>
</ol>
<ul>
<li>클라우드 환경에서는 데이터가 여러 네트워크 노드를 거치며 전송, 로컬 환경에 비해 더 많은 네트워크 지연이 발생할 수 있다.</li>
</ul>
<ol start="2">
<li>인코딩 및 트랜스 코딩</li>
</ol>
<ul>
<li>영상을 인코딩하고 다양한 해상도로 트랜스 코딩하는 과정에서 처리 시간이 발생한다.</li>
<li>클라우드 환경에서는 이 과정이 더 복잡해지고, 리소스 제한으로 인해 시간이 더 걸릴 수 있다.</li>
</ul>
<ol start="3">
<li>HLS 프로토콜 자체의 특성</li>
</ol>
<ul>
<li>HLS는 영상을 여러 짧은 영상 세그먼트로 비디오 스트림을 분할한다.</li>
<li>클라이언트는 이 세그먼트들을 다운로드하여 재생하는 방식</li>
<li>이 방식은 네트워크 상황에 유연하게 대응할 수 있지만, 세그먼트 길이와 버퍼링으로 인해 딜레이가 발생한다.</li>
</ul>
<ol start="4">
<li>서버 설정</li>
</ol>
<ul>
<li><code>hls_fragment</code>, <code>hls_playlist_length</code> 설정은 세그먼트 길이와 재생 목록에 있는 세그먼트의 수를 결정한다.</li>
<li>이러한 설정에 따라 딜레이가 생길 수 있다.</li>
</ul>
<h2 id="줄이는-법">줄이는 법</h2>
<ol>
<li>세그먼트 길이 줄이기</li>
</ol>
<ul>
<li>HLS 세그먼트의 길이를 줄이면 딜레이를 감소시킬 수 있다. </li>
<li>너무 짧게 설정하면 네트워크 변동성에 따른 버퍼링이 증가할 수 있다.</li>
</ul>
<ol start="2">
<li>최적화된 인코딩 설정 사용</li>
</ol>
<ul>
<li>인코딩 프로세스를 최적화하여 처리 시간을 줄이고, 가능하다면 하드웨어 가속을 사용</li>
</ul>
<ol start="3">
<li><p>CDN 사용</p>
</li>
<li><p>서버와 클라이언트 최적화</p>
</li>
<li><p>로우 레이턴시 HLS(LHLS) 고려</p>
</li>
</ol>
<ul>
<li>최신 스트리밍 기술인 로우 레이턴시 HLS를 사용하면 전통적인 HLS 대비 훨씬 낮은 딜레이로 스트리밍할 수 있다. 이 기술은 세그먼트 전송을 더 효율적으로 처리</li>
<li><blockquote>
<p>모든 환경과 네트워크 조건에서 일관된 결과를 보장하기 어려울 수 있음</p>
</blockquote>
</li>
</ul>
<blockquote>
<p>우리의 서비스는 Docker로 현재 배포된 상황이고 NginX - rtmp 모듈을 사용중이다.</p>
</blockquote>
<h2 id="우리-서비스-상황에서의-해결법">우리 서비스 상황에서의 해결법</h2>
<ul>
<li>nginx 설정과 Docker 환경을 최적화하여 스트리밍 성능을 개선할 수 있는 몇 가지 방법</li>
</ul>
<ol>
<li>HLS 세그먼트 설정 조정</li>
</ol>
<ul>
<li>hls_fragment와 hls_playlist_length 값을 조정</li>
<li>현재 설정은 각각 2초와 16초로 되어 있는데, 이는 세그먼트의 길이와 재생 목록의 총 길이를 의미</li>
<li>세그먼트 길이를 줄이면 딜레이를 감소시킬 수 있지만, 너무 작은 값은 버퍼링 문제를 야기할 수 있음 -&gt; 최적의 값을 찾아야 함.</li>
</ul>
<ol start="2">
<li>인코딩 프리셋 조정</li>
</ol>
<ul>
<li>preset 값을 superfast에서 ultrafast로 변경</li>
<li>인코딩 속도를 높이지만, 비트레이트 대비 화질이 다소 떨어질 수 있으므로, 품질과 딜레이 간의 균형을 고려해야한다.</li>
</ul>
<ol start="3">
<li>인코딩 해상도와 비트레이트 최적화</li>
</ol>
<ul>
<li>송출되는 영상의 해상도와 비트레이트를 최적화하여 네트워크 부하를 줄이고 인코딩 시간을 단축할 수 있다.</li>
<li>obs studio 에서 조정해보았을때, 로컬환경에서는 7초까지 줄여진 것으로 확인된다.</li>
</ul>
<ol start="4">
<li>Docker 환경 최적화</li>
</ol>
<ul>
<li>Docker 컨테이너의 리소스 할당을 조정하여 인코딩 및 스트리밍 성능을 향상시킬 수 있다. </li>
<li>docker-compose.yml 파일에서 컨테이너에 더 많은 CPU 코어나 메모리를 할당할 수 있습니다.</li>
</ul>
<ol start="5">
<li>네트워크 최적화
클라우드 환경에서의 네트워크 지연을 최소화하기 위해, 스트리밍 서버의 지리적 위치를 사용자에게 가깝게 선택하거나, CDN을 활용하여 콘텐츠 전송 속도를 개선</li>
</ol>
<h2 id="로우레이턴시-hls-트랜스코딩">로우레이턴시 HLS, 트랜스코딩</h2>
<ul>
<li>여러가지 자료들을 조사해봤을때, 더 짧은 지연시간을 제공하는 로우 레이턴시 HLS와, 파일을 압축되지 않은 형식으로 디인코딩하고 다른 형식으로 변환하는 프로세스인 트랜스 코딩으로 지연시간을 해결할 수 있다지만</li>
</ul>
<p>로우 레이턴시 HLS 자체는 호환성이 뛰어나지 않고(최신기술), 트랜스 코딩과 같이 이루어질경우 서버의 부하, 비용 증가 측면도 고려해야한다.</p>
<p>그렇다면 트랜스 코딩으로 조금이나마 지연시간을 줄여보는 방식은 어떨지 실험해봐야겠다.</p>
<h2 id="지연시간을-줄이기-위해서-했던-방식">지연시간을 줄이기 위해서 했던 방식</h2>
<ol>
<li>인코딩 해상도와 비트레이트 최적화</li>
</ol>
<ul>
<li>결과: obs studio 에서 조정해보았을때, 로컬환경에서는 7초까지 줄여진 것으로 확인된다.</li>
</ul>
<ol start="2">
<li>트랜스 코딩 </li>
</ol>
<ul>
<li><p>방식
1) 프레임레이트 조정</p>
<pre><code>- 저해상도 스트림에서 프레임레이트를 낮추어 네트워크 대역폭을 절약할 수 있다. 
- 예를 들어, 240p 저해상도 스트림에서 15fps를 유지하는 것은 합리적인 선택</code></pre><p>2) 인코딩 프리셋 조정</p>
<pre><code>- 인코딩 시간과 품질 사이의 균형을 찾기 위해 superfast 외에 다른 프리셋(ultrafast 등)을 적용해보기
- 프리셋을 변경하면 인코딩 속도와 결과물의 품질에 영향을 줄 수 있다.</code></pre><p>3) 비트레이트 조정</p>
<pre><code>- 클라이언트의 네트워크 환경에 따라 비트레이트를 조정할 수 있다. 
- 높은 비트레이트는 품질을 개선하지만, 네트워크 대역폭 요구사항을 증가시킨다.    </code></pre></li>
</ul>
<ul>
<li>결과
<a href="https://velog.io/@choisooyoung-dev/TIL-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D-%EC%A7%80%EC%97%B0-%EB%AC%B8%EC%A0%9C-%ED%8A%B8%EB%9E%9C%EC%8A%A4%EC%BD%94%EB%94%A9-%EC%84%A4%EC%A0%95-%EC%A1%B0%EC%A0%95-%EA%B2%B0%EA%B3%BC">https://velog.io/@choisooyoung-dev/TIL-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D-%EC%A7%80%EC%97%B0-%EB%AC%B8%EC%A0%9C-%ED%8A%B8%EB%9E%9C%EC%8A%A4%EC%BD%94%EB%94%A9-%EC%84%A4%EC%A0%95-%EC%A1%B0%EC%A0%95-%EA%B2%B0%EA%B3%BC</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 세션 기반 인증과 토큰 기반 인증의 차이]]></title>
            <link>https://velog.io/@choisooyoung-dev/TIL-%EC%84%B8%EC%85%98-%EA%B8%B0%EB%B0%98-%EC%9D%B8%EC%A6%9D%EA%B3%BC-%ED%86%A0%ED%81%B0-%EA%B8%B0%EB%B0%98-%EC%9D%B8%EC%A6%9D%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@choisooyoung-dev/TIL-%EC%84%B8%EC%85%98-%EA%B8%B0%EB%B0%98-%EC%9D%B8%EC%A6%9D%EA%B3%BC-%ED%86%A0%ED%81%B0-%EA%B8%B0%EB%B0%98-%EC%9D%B8%EC%A6%9D%EC%9D%98-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Mon, 05 Feb 2024 17:21:14 GMT</pubDate>
            <description><![CDATA[<p>세션 기반 인증과 토큰 기반 인증은 사용자 인증을 처리하는 두 가지 주요 방법입니다. 각각의 방법은 사용자의 신원을 확인하고, 그 신원에 기반하여 사용자가 시스템의 자원에 접근할 수 있도록 허용하는 메커니즘을 제공합니다. 그러나 이 두 방법은 상태 관리와 클라이언트-서버 간의 인증 정보 교환 방식에서 근본적인 차이를 가집니다.</p>
<p>세션 기반 인증
세션 기반 인증에서는 서버가 사용자의 로그인 정보를 받아 인증 과정을 수행한 후, 사용자에 대한 세션을 생성하고 이를 서버에 저장합니다. 사용자의 브라우저는 세션 식별자(보통 쿠키에 저장됨)를 받게 되며, 이후의 모든 요청에 이 식별자를 포함시켜 서버에 보냅니다. 서버는 이 식별자를 사용하여 저장된 세션을 조회하고, 해당 세션에 연결된 사용자의 인증 상태와 권한을 확인합니다.</p>
<p>장점: 상태가 서버에 의해 관리되므로, 서버는 사용자의 로그인 상태를 정확하게 추적할 수 있습니다. 또한, 세션 쿠키는 비교적 관리하기 쉽습니다.
단점: 모든 사용자 정보가 서버 메모리에 저장되기 때문에, 대규모 시스템에서는 서버의 자원을 많이 소모할 수 있습니다. 또한, 서버 환경이 확장될 때 세션의 일관성을 유지하기 위한 추가적인 인프라가 필요할 수 있습니다.
토큰 기반 인증
토큰 기반 인증에서는 서버가 사용자의 로그인 정보를 확인한 후, 서명된 토큰(예: JWT - JSON Web Tokens)을 생성하여 사용자에게 반환합니다. 이 토큰은 사용자의 인증 정보와 권한을 암호화한 정보를 포함하고 있으며, 사용자는 이후의 모든 요청에 이 토큰을 포함시켜 서버에 전송합니다. 서버는 토큰의 유효성을 검증하고, 토큰에 포함된 정보를 사용하여 사용자의 권한을 확인합니다.</p>
<p>장점: 세션 상태를 서버에 저장할 필요가 없으므로, 서버 리소스의 부담이 줄어들고, 확장성이 향상됩니다. 또한, 토큰은 다양한 플랫폼과 서비스 간의 인증에 용이하게 사용될 수 있습니다.
단점: 토큰이 탈취되면, 탈취자는 토큰의 유효 기간 동안 사용자의 권한을 가질 수 있습니다. 따라서, 토큰의 보안과 관리에 주의가 필요합니다.
결론
세션 기반 인증은 전통적인 방법으로, 서버 측에서 사용자의 상태를 관리하는 데 초점을 맞춥니다. 반면, 토큰 기반 인증은 상태를 클라이언트 측에서 관리하며, 서버의 확장성과 서비스 간의 연동성에 유리한 방식입니다. 각 방식은 특정한 시나리오와 요구 사항에 따라 선택</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] ORM 복잡한 쿼리 해결법]]></title>
            <link>https://velog.io/@choisooyoung-dev/TIL-ORM-%EB%B3%B5%EC%9E%A1%ED%95%9C-%EC%BF%BC%EB%A6%AC-%ED%95%B4%EA%B2%B0%EB%B2%95</link>
            <guid>https://velog.io/@choisooyoung-dev/TIL-ORM-%EB%B3%B5%EC%9E%A1%ED%95%9C-%EC%BF%BC%EB%A6%AC-%ED%95%B4%EA%B2%B0%EB%B2%95</guid>
            <pubDate>Mon, 05 Feb 2024 01:27:37 GMT</pubDate>
            <description><![CDATA[<p>ORM의 Query Builder 사용: 대부분의 ORM은 Query Builder를 제공하여 SQL 쿼리를 동적으로 작성할 수 있는 기능을 제공합니다. 이를 통해 복잡한 쿼리를 더욱 간단하게 작성할 수 있습니다.</p>
<p>Stored Procedure 사용: 저장 프로시저는 데이터베이스에 미리 정의된 SQL 쿼리를 저장하는 방법을 제공합니다. 복잡한 로직이나 연산을 데이터베이스 측에서 처리하고자 할 때 유용합니다. ORM과 함께 사용할 수 있지만, 데이터베이스 종속성을 증가시킬 수 있으므로 주의가 필요합니다.</p>
<p>인덱스 및 성능 튜닝: 데이터베이스의 쿼리 성능을 향상시키기 위해 인덱스를 추가하고 쿼리를 튜닝할 수 있습니다. 복잡한 쿼리의 경우 성능 튜닝이 중요하며, ORM이 생성하는 쿼리의 실행 계획을 분석하여 최적화할 수 있습니다.</p>
<p>ORM의 성능 향상 기능 활용: 일부 ORM은 성능 향상을 위해 캐싱, 지연 로딩 등의 기능을 제공합니다. 이러한 기능을 활용하여 복잡한 쿼리의 부하를 줄일 수 있습니다.</p>
<p>데이터베이스 샤딩: 데이터베이스 샤딩은 대규모 데이터베이스를 분할하여 처리 부하를 분산시키는 방법입니다. 복잡한 쿼리에 대한 성능을 향상시키는데 도움이 될 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] OSI 7계층]]></title>
            <link>https://velog.io/@choisooyoung-dev/TIL-OSI-7%EA%B3%84%EC%B8%B5</link>
            <guid>https://velog.io/@choisooyoung-dev/TIL-OSI-7%EA%B3%84%EC%B8%B5</guid>
            <pubDate>Thu, 01 Feb 2024 21:05:33 GMT</pubDate>
            <description><![CDATA[<p>1984년 국제표준화기구(ISO)에서 개발한 모델로써, 네트워크 프로토콜 디자인과 통신 과정을 7개의 계층으로
구분하여 만든 &quot;표준 규격&quot;이다. 초창기의 네트워크는 각 컴퓨터마다 시스템이 달랐기 때문에 하드웨어와
소프트웨어의 논리적인 변경 없이 통신할 수 있는 표준 모델이 나타나게 되었다.</p>
<p>통신이 일어나는 과정을 7단계로 크게 구분하여, 단계별로 파악이 가능
OSI 참조 모델 혹은 OSI 7 계층이라 불림
OSI (Open System Interconnection) : 개방형 시스템 =&gt; 누구나 참조 및 부가적인 추가 가능
컴퓨팅 장치나 네트워킹 장치를 만들 때 이 모델을 참조해서 모든 통신 장치를 만든다.
각 계층은 독립적인 모듈로 구성되어 있음
각 계층은 상하 계급 구조를 가지고 있음
상위 계층의 프로토콜이 제대로 동작하기 위해서는 하위의 모든 계층에 문제가 없어야 한다.
물리 계층 : 하드웨어 /  데이터링크 계층 : 하드웨어 + 소프트웨어 / 3 계층부터는 소프트웨어로 구성</p>
<p><img src="https://velog.velcdn.com/images/choisooyoung-dev/post/1818bdf5-fea6-4ecd-854a-335c89f0a0c2/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] GET, POST의 개념, 데이터 흐름]]></title>
            <link>https://velog.io/@choisooyoung-dev/TIL-GET-POST%EC%9D%98-%EA%B0%9C%EB%85%90-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%9D%90%EB%A6%84</link>
            <guid>https://velog.io/@choisooyoung-dev/TIL-GET-POST%EC%9D%98-%EA%B0%9C%EB%85%90-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%9D%90%EB%A6%84</guid>
            <pubDate>Wed, 31 Jan 2024 16:35:46 GMT</pubDate>
            <description><![CDATA[<h2 id="get">GET</h2>
<h2 id="post">POST</h2>
<h2 id="데이터-흐름">데이터 흐름</h2>
]]></description>
        </item>
    </channel>
</rss>