<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>GDORI</title>
        <link>https://velog.io/</link>
        <description>하루 최소 1시간이라도 공부하자..</description>
        <lastBuildDate>Tue, 11 Feb 2025 13:14:40 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>GDORI</title>
            <url>https://velog.velcdn.com/images/r_louis/profile/dc2be99e-267c-4eee-9a8c-34a4ffc8a7f7/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. GDORI. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/r_louis" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[TIL] 25.02.11 TUE]]></title>
            <link>https://velog.io/@r_louis/TIL-25.02.11-TUE</link>
            <guid>https://velog.io/@r_louis/TIL-25.02.11-TUE</guid>
            <pubDate>Tue, 11 Feb 2025 13:14:40 GMT</pubDate>
            <description><![CDATA[<h3 id="spring-boot에서-lets-encrypt-인증서-적용-p12-변환-및-설정">Spring Boot에서 Let&#39;s Encrypt 인증서 적용 (P12 변환 및 설정)</h3>
<p>이번 Spring Boot 애플리케이션에서 기존 Node.js에서 사용한 Let&#39;s Encrypt 인증서를 적용했다.
P12(Keystore) 형식으로 변환하여 Spring Boot의 <code>application.properties</code>에 적용하면 된다.</p>
<h3 id="인증서-발급">인증서 발급</h3>
<p>인증서는 기존에 받은게 있어 재활용 할 예정이지만 나중에 보고 쓸수도 있으니 발급부터..<code>certbot</code>을 이용하여 발급한다.</p>
<pre><code class="language-bash">sudo certbot certonly --standalone -d yourdomain.com</code></pre>
<p>발급이 완료되면 인증서와 키 파일이 생성되고 기본 경로는 다음과 같다.</p>
<ul>
<li>인증서: <code>/etc/letsencrypt/live/yourdomain.com/fullchain.pem</code></li>
<li>개인 키: <code>/etc/letsencrypt/live/yourdomain.com/privkey.pem</code></li>
</ul>
<h3 id="p12keystore-파일로-변환">P12(Keystore) 파일로 변환</h3>
<p>Spring Boot에서 인증서를 사용하려면 <code>.p12</code> 형식으로 변환해야 하는데 <code>openssl</code>을 이용하여 변환할 수 있다.</p>
<pre><code class="language-bash">openssl pkcs12 -export \
    -inkey /etc/letsencrypt/live/yourdomain.com/privkey.pem \
    -in /etc/letsencrypt/live/yourdomain.com/fullchain.pem \
    -out keystore.p12 \
    -name tomcat \
    -CAfile chain.pem \
    -caname root</code></pre>
<p>비밀번호를 입력하라고 뜰텐데 원하는 비밀번호를 적어주면 된다.</p>
<h3 id="spring-boot-applicationproperties-설정">Spring Boot application.properties 설정</h3>
<p>생성한 <code>keystore.p12</code> 파일을 Spring Boot에서 사용하려면, <code>application.properties</code> 또는 <code>application.yml</code>에 설정을 추가해야 한다.</p>
<h4 id="🔹-applicationproperties"><strong>🔹 application.properties</strong></h4>
<pre><code class="language-sh">server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-type=PKCS12
server.ssl.key-store-password=지정한 비밀번호</code></pre>
<p><code>keystore.p12</code>를 <code>src/main/resources</code>에 위치시켜야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 25.02.09 SUN - 25.02.10 MON]]></title>
            <link>https://velog.io/@r_louis/TIL-25.02.09-SUN-25.02.10-MON</link>
            <guid>https://velog.io/@r_louis/TIL-25.02.09-SUN-25.02.10-MON</guid>
            <pubDate>Mon, 10 Feb 2025 21:15:07 GMT</pubDate>
            <description><![CDATA[<h3 id="간단한-스프링부트-서버">간단한 스프링부트 서버</h3>
<p>일요일에 간단하게 강의 듣고 문제 저장 조회만 구현했다. 데이터 저장은 JPA 스프링데이터를 이용했다.
스프링부트를 체험느낌으로 해봤는데, 배워야 할 내용이 광범위해서 짧은 시간내에 마스터하기는 쉽지 않을 것 같으나 배워두면 좋을 것 같긴 하다. 자동 설정 기능을 제공해서 비즈니스 로직 구현에 집중할 수 있고 지원하는 프레임워크가 많으며 활발한 커뮤니티로 문제 해결이나 새로운 기능 학습에 많은 도움을 받을 수 있을 것 같다.</p>
<p>잘못된 방식일 수 있으니 코드 일부만 올린다..</p>
<h4 id="config">config</h4>
<p>스프링 컨테이너 안 빈으로 등록하여 싱글톤으로 가져다쓸 수 있게 스프링부트 설정을 할 수 있다. </p>
<pre><code class="language-java">@Configuration
public class SpringConfig {
    private final QuestionRepository questionRepository;
    private final KeywordRepository keywordRepository;

    @Autowired
    public SpringConfig(QuestionRepository questionRepository, KeywordRepository keywordRepository) {
        this.questionRepository = questionRepository;
        this.keywordRepository = keywordRepository;
    }

    @Bean
    public KeywordService keywordService() {
        return new KeywordService(this.questionRepository, this.keywordRepository);
    }

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(Include.NON_NULL);
        return objectMapper;
    }
}</code></pre>
<h4 id="controller">controller</h4>
<pre><code class="language-java">@RestController
public class KeywordController {
    private final KeywordService keywordService;

    @Autowired
    public KeywordController(KeywordService keywordService) {
        this.keywordService = keywordService;
    }

    @GetMapping(&quot;/keyword&quot;)
    public ResponseEntity&lt;Message&gt; loadKeyword(){

        List&lt;QuestionDto&gt; result = keywordService.getAllQuestions();


        // 헤더 생성 및 설정
        HttpHeaders headers= new HttpHeaders();
        headers.setContentType(new MediaType(&quot;application&quot;, &quot;json&quot;, StandardCharsets.UTF_8));

        // 반환 메세지 인스턴스 생성
        Message message = new Message(StatusEnum.OK,&quot;성공&quot;,result);

        return new ResponseEntity&lt;Message&gt;(message,headers, HttpStatus.OK);
    }


    @PostMapping(&quot;/keyword&quot;)
    public ResponseEntity&lt;Message&gt; addKeyword(@RequestBody Question question){

        // 키워드 등록
        QuestionEntity result = keywordService.saveQuestion(question);

        // 헤더 생성 및 설정
        HttpHeaders headers= new HttpHeaders();
        headers.setContentType(new MediaType(&quot;application&quot;, &quot;json&quot;, StandardCharsets.UTF_8));

        // 반환 메세지 인스턴스 생성
        Message message = new Message(StatusEnum.OK,&quot;성공&quot;,result);


        return new ResponseEntity&lt;Message&gt;(message,headers, HttpStatus.OK);
    }
}</code></pre>
<h4 id="service">service</h4>
<pre><code class="language-java">public class KeywordService {


    private final QuestionRepository questionRepository;
    private final KeywordRepository keywordRepository;

    public KeywordService(QuestionRepository questionRepository, KeywordRepository keywordRepository) {
        this.questionRepository = questionRepository;
        this.keywordRepository = keywordRepository;
    }

    public List&lt;QuestionDto&gt; getAllQuestions() {
        return questionRepository.findAll().stream().map(question -&gt; {
            QuestionDto dto = new QuestionDto();
            dto.setQuestionId(question.getQuestion_id());
            dto.setTitle(question.getTitle());
            dto.setDescription(question.getDescription());
            dto.setKeywords(question.getKeywords().stream().map(KeywordEntity::getTag).collect(Collectors.toList()));
            return dto;
        }).collect(Collectors.toList());
    }

    public QuestionEntity saveQuestion(Question keyword) {

        // question 엔터티 생성
        QuestionEntity question = new QuestionEntity();
        question.setTitle(keyword.getTitle());
        question.setDescription(keyword.getDescription());

        // question 저장
        QuestionEntity savedQuestion = questionRepository.save(question);

        String[] keywords = keyword.getKeywords();

        // keyword 저장
        Arrays.stream(keywords).forEach(tag -&gt; {
            KeywordEntity k = new KeywordEntity();
            k.setTag(tag);
            k.setQuestion(savedQuestion);
            keywordRepository.save(k);
        });

        return savedQuestion;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 25.02.08 SAT]]></title>
            <link>https://velog.io/@r_louis/TIL-25.02.08-SAT</link>
            <guid>https://velog.io/@r_louis/TIL-25.02.08-SAT</guid>
            <pubDate>Sat, 08 Feb 2025 15:06:28 GMT</pubDate>
            <description><![CDATA[<h3 id="할-거-많은데-spring-boot">할 거 많은데 Spring Boot?</h3>
<p>친구가 동생 시험 준비를 위하여 간단한 문장 맞추는 어플리케이션을 만든다고 하여 백엔드를 구성해주기로 했는데 Node.js로 하면 금방 끝나겠지만 또 새로운 학문을 핥아보는 것을 좋아하는 변태적 취향때문에 스프링부트로 만들어 보기로 했다.
아예 해본적이 없어 오늘은 간단하게 구조와 작동원리 정도만 익히고 1~2일정도만 투자해 볼 생각이다.</p>
<h3 id="스프링-부트란">스프링 부트란?</h3>
<p>자바 기반의 프레임워크인 스프링에서 개발을 더 빠르고 쉽게 할 수 있도록 돕는 도구로 스프링부트를 사용하면 복잡한 설정 없이 바로 실행 가능한 애플리케이션을 만들 수 있다. 
또 개발자가 명시적으로 설정하지 않아도 필요한 설정을 자동으로 해주고 Tomcat 웹 서버가 내장되어 있어 서버 설치없이 애플리케이션 실행이 가능하다. 그리고 기본적으로 설정된 프로젝트 구조와 템플릿을 제공하여 스프링 설정에 대해 신경 쓸 필요 없이 개발을 시작할 수 있다는 장점이 있다.</p>
<h3 id="스프링-이니셜라이저spring-initializr-사용">스프링 이니셜라이저(Spring Initializr) 사용</h3>
<p>스프링 이니셜라이저(<a href="https://start.spring.io/)%EC%97%90%EC%84%9C">https://start.spring.io/)에서</a> 프로젝트를 생성할 수 있다. 여기에서 기본 프로젝트 설정(언어, 빌드 도구, 의존성 등)을 선택한 후 다운로드하여 바로 시작할 수 있다.
<img src="https://velog.velcdn.com/images/r_louis/post/615fd75b-25ae-4f8d-8208-7679b53055aa/image.png" alt=""></p>
<p>필요한 정보를 입력하고 GENERATE를 누르면 압축파일을 다운받을 수 있는데, 이게 프로젝트이다.
이클립스나 인텔리제이를 이용하여 열면 된다.
<img src="https://velog.velcdn.com/images/r_louis/post/f48299ff-8cfa-495f-8d2e-bfc249889d9e/image.png" alt=""></p>
<h3 id="srcmainjava프로젝트-패키지네임application">src/main/java/프로젝트 패키지네임/Application</h3>
<p>기본적으로 <code>@SpringBootApplication</code> 어노테이션을 사용한 메인 클래스가 생성되어 있고 이 클래스는 스프링부트 애플리케이션을 실행하는 시작점이 된다.</p>
<pre><code class="language-java">   @SpringBootApplication
   public class Application {
       public static void main(String[] args) {
           SpringApplication.run(Application.class, args);
       }
   }</code></pre>
<h3 id="컨트롤러">컨트롤러</h3>
<p>간단한 웹 애플리케이션을 만들려면 <code>@RestController</code>나 <code>@Controller</code>를 사용하여 웹 요청을 처리할 수 있다.</p>
<pre><code class="language-java">package com.example.san.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HelloController {

    @GetMapping(&quot;hello&quot;)
    public String hello(Model model){
        model.addAttribute(&quot;data&quot;,&quot;hello!&quot;);
        return &quot;hello&quot;;
    }
}
</code></pre>
<p>주말 간 기분전환 겸 새로운 공부 좀 하고 간단한 서버 하나 만든 후 다시 원래 하던걸로 돌아가야겠다..
몸이 다섯개였으면 좋겠다. ㅋㅋ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 25.02.07 FRI]]></title>
            <link>https://velog.io/@r_louis/TIL-25.02.07-FRI</link>
            <guid>https://velog.io/@r_louis/TIL-25.02.07-FRI</guid>
            <pubDate>Fri, 07 Feb 2025 13:16:47 GMT</pubDate>
            <description><![CDATA[<h3 id="타입을-왜-제대로-알아야-할까">타입을 왜 제대로 알아야 할까?</h3>
<ul>
<li>타입을 명확하게 정의하면 코드의 안정성 및 가독성이 향상된다.  </li>
<li>잘못된 타입을 사용하면 런타임 오류가 발생할 가능성이 높다.  </li>
<li>타입 안정성을 확보하면 협업이 쉬워지고, 유지보수 비용이 줄어든다.  </li>
</ul>
<hr>
<h3 id="typescript의-기본-타입">TypeScript의 기본 타입</h3>
<h4 id="boolean-true--false">Boolean (<code>true | false</code>)</h4>
<ul>
<li>두 가지 상태(참/거짓)를 표현하는데 사용된다.  <pre><code class="language-ts">function isValidPassword(password: string): boolean {
return password.length &gt;= 8;
}</code></pre>
</li>
</ul>
<h4 id="number-정수-실수-2진수-8진수-16진수-등">Number (<code>정수, 실수, 2진수, 8진수, 16진수 등</code>)</h4>
<ul>
<li>JavaScript와 다르게 TypeScript에서는 모든 숫자가 <code>number</code> 타입으로 통합된다.  <pre><code class="language-ts">function calculateArea(radius: number): number {
return Math.PI * radius * radius;
}</code></pre>
</li>
</ul>
<h4 id="string-문자열">String (<code>문자열</code>)</h4>
<ul>
<li>작은 따옴표, 큰 따옴표, 백틱(``)을 사용해 문자열을 표현한다.  </li>
<li>템플릿 리터럴을 활용하면 문자열을 쉽게 조합할 수 있다.  <pre><code class="language-ts">function greet(name: string): string {
return `안녕, ${name}!`;
}</code></pre>
</li>
</ul>
<h4 id="array-배열">Array (<code>배열</code>)</h4>
<ul>
<li>동일한 타입의 여러 값을 저장할 때 사용된다.  <pre><code class="language-ts">const numbers: number[] = [1, 2, 3, 4, 5];</code></pre>
</li>
</ul>
<h4 id="tuple-고정된-타입과-순서를-가지는-배열">Tuple (<code>고정된 타입과 순서를 가지는 배열</code>)</h4>
<ul>
<li>서로 다른 타입을 순서대로 가질 수 있다.  <pre><code class="language-ts">const person: [string, number, boolean] = [&#39;Alice&#39;, 25, true];</code></pre>
</li>
</ul>
<h4 id="enum-상수-값을-정의할-때-사용">Enum (<code>상수 값을 정의할 때 사용</code>)</h4>
<ul>
<li>명확하게 관련된 값들을 그룹화하는데 유용하다.  <pre><code class="language-ts">enum UserRole { ADMIN, USER, GUEST }
const user: UserRole = UserRole.ADMIN;</code></pre>
</li>
</ul>
<h3 id="const와-readonly">const와 readonly</h3>
<ul>
<li><code>const</code> → 값 재할당이 불가능한 상수  </li>
<li><code>readonly</code> → 객체 속성을 변경할 수 없도록 설정  <pre><code class="language-ts">class Person {
readonly name: string;
constructor(name: string) {
  this.name = name;
}
}</code></pre>
</li>
</ul>
<h3 id="any-vs-unknown-vs-union-타입">any vs unknown vs union 타입</h3>
<ul>
<li>any (<code>아무 타입이나 저장 가능하지만 지양해야 함</code>)  </li>
<li>unknown (<code>any보다 안전하며, 타입 체크 후 사용할 수 있음</code>)  </li>
<li>union (<code>여러 타입을 동시에 허용</code>)  <pre><code class="language-ts">type StringOrNumber = string | number;
function processValue(value: StringOrNumber) {
if (typeof value === &#39;string&#39;) console.log(&quot;String:&quot;, value);
else console.log(&quot;Number:&quot;, value);
}</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 25.02.05 WED]]></title>
            <link>https://velog.io/@r_louis/TIL-25.02.05-WED-szyp9nyg</link>
            <guid>https://velog.io/@r_louis/TIL-25.02.05-WED-szyp9nyg</guid>
            <pubDate>Wed, 05 Feb 2025 15:03:18 GMT</pubDate>
            <description><![CDATA[<h3 id="typescript-컴파일러-tsc">Typescript 컴파일러, tsc</h3>
<p>컴파일러는 프로그래밍 언어의 소스 코드를 다른 언어로 변환하는 도구로, 타입 검사와 코드 변환을 담당한다.
tsc는 TypeScript 컴파일러로, TypeScript를 JavaScript로 변환한다.
JavaScript는 동적 언어라서 기계어로 변환될 필요가 없고, JavaScript 엔진이 직접 실행한다.</p>
<h4 id="주요-명령어">주요 명령어</h4>
<p><code>tsc --init</code>: tsconfig.json 파일 생성
<code>tsc index.ts</code>: index.ts 파일 컴파일
<code>tsc src/*.ts</code>: src 디렉토리의 모든 TypeScript 파일 컴파일</p>
<h3 id="tsconfigjson">tsconfig.json</h3>
<p>tsconfig.json은 TypeScript 프로젝트의 설정 파일로, 컴파일 옵션과 입력 파일을 정의한다.</p>
<h4 id="중요한-compileroptions">중요한 compilerOptions</h4>
<p><code>target</code>: 변환할 JavaScript 버전 설정 (es5, es2016 등)
<code>module</code>: JavaScript 모듈 시스템 설정
<code>outDir</code>: 컴파일된 파일이 저장될 디렉토리 지정
<code>strict</code>: 엄격한 타입 검사 활성화 (권장)
<code>sourceMap</code>: 소스 맵 생성 (개발 환경에서 유용)</p>
<h3 id="dts-파일-알아보기">.d.ts 파일 알아보기</h3>
<p>.d.ts 파일은 JavaScript 라이브러리에 대한 TypeScript 타입 정보를 제공하여, 기존 JavaScript 라이브러리도 TypeScript 프로젝트에서 사용할 수 있게 돕는다.
<code>@types</code> 라이브러리는 외부 JavaScript 라이브러리의 타입 정보를 제공한다.
TypeScript에서 JavaScript 라이브러리를 사용할 때 <code>.d.ts</code> 파일만 있으면 기존 JavaScript 코드를 수정하지 않고도 타입 안전성을 확보할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 25.02.04 TUE]]></title>
            <link>https://velog.io/@r_louis/TIL-25.02.04-TUE</link>
            <guid>https://velog.io/@r_louis/TIL-25.02.04-TUE</guid>
            <pubDate>Tue, 04 Feb 2025 14:56:42 GMT</pubDate>
            <description><![CDATA[<h3 id="소프트웨어의-특징">소프트웨어의 특징</h3>
<p>소프트웨어는 하드웨어와 달리 물리적인 형태가 없으며, 다음과 같은 주요 특징을 가진다.</p>
<ul>
<li>무형성: 물리적인 형태가 없으며, 코드로 존재.</li>
<li>복잡성: 많은 기능을 제공하고, 다양한 환경에서 작동해야 하므로 복잡한 구조를 가짐.</li>
<li>변경 용이성: 하드웨어에 비해 수정 및 업그레이드가 쉬움.</li>
<li>재사용성: 모듈화되어 있을 경우 코드 재사용이 가능.</li>
<li>비가시성: 하드웨어처럼 눈에 보이는 형태가 없어 관리가 어려움.</li>
</ul>
<hr>
<h3 id="소프트웨어-위기">소프트웨어 위기</h3>
<p>소프트웨어 위기는 소프트웨어 개발의 복잡성이 증가하면서 발생하는 문제</p>
<ul>
<li>품질 저하: 소프트웨어가 점점 더 복잡해져 품질 보장이 어려워짐.</li>
<li>개발 비용 증가: 소프트웨어의 크기와 복잡도가 커짐에 따라 개발, 유지보수 비용이 급증.</li>
<li>프로젝트 지연: 프로젝트 완료 기한을 맞추기 어려운 경우가 많음.</li>
</ul>
<hr>
<h3 id="소프트웨어-공학-기본-원칙">소프트웨어 공학 기본 원칙</h3>
<p>소프트웨어 공학의 기본 원칙은 효율적이고 품질 높은 소프트웨어 개발을 위한 지침을 제공</p>
<ul>
<li>구조적 접근법: 소프트웨어 개발을 체계적으로 접근.</li>
<li>모듈화: 기능별로 나누어 관리하고, 재사용성을 높임.</li>
<li>유지보수성: 소프트웨어가 개발된 후에도 수정 및 개선이 용이하게 설계해야 함.</li>
<li>표준화: 개발 과정에서 일관된 절차와 표준을 따름.</li>
<li>문서화: 개발 과정과 결과물을 명확하게 기록하여 추후 수정이나 유지보수가 용이하도록 함.</li>
</ul>
<hr>
<h3 id="재공학">재공학</h3>
<p>재공학은 기존의 시스템을 분석하고 개선하는 과정으로 보통 오래된 소프트웨어 시스템을 최신 기술로 개선하거나, 기존 시스템을 재설계하는 과정이다. 주요 목적은 효율성을 높이고, 시스템의 품질을 향상시키는 것이다.</p>
<hr>
<h3 id="역공학">역공학</h3>
<p>역공학은 기존의 소프트웨어나 하드웨어를 분해하여 동작 원리나 구조를 분석하는 과정으로 소스 코드가 없을 때 소프트웨어를 분석하여 기능 및 설계 구조를 파악하는 데 사용된다. 법적인 문제가 발생할 수 있으므로 주의가 필요하다.</p>
<hr>
<h3 id="case">CASE</h3>
<p>CASE 도구는 소프트웨어 개발을 지원하는 자동화된 도구다. 이를 사용하여 소프트웨어 설계, 코드 생성, 테스트 등을 효율적으로 관리할 수 있다.</p>
<ul>
<li>자동화된 설계: 시스템의 구조를 자동으로 생성.</li>
<li>문서화: 설계 및 코드를 문서화하여 추후 참조가 가능하게 함.</li>
<li>프로젝트 관리: 개발 진행 상황을 관리하고 추적.</li>
</ul>
<hr>
<h3 id="소프트웨어-생명주기">소프트웨어 생명주기</h3>
<p>소프트웨어 생명주기는 소프트웨어 개발 과정의 모든 단계를 포함하는 개념이다.</p>
<ol>
<li>요구 사항 분석: 소프트웨어가 충족해야 할 요구 사항을 정의.</li>
<li>설계: 시스템의 구조를 설계.</li>
<li>구현: 실제 코드 작성.</li>
<li>테스트: 소프트웨어가 요구 사항을 충족하는지 확인.</li>
<li>배포: 실제 환경에 소프트웨어를 배포.</li>
<li>유지보수: 배포 후 소프트웨어의 수정 및 개선.</li>
</ol>
<hr>
<h3 id="애자일-방법론">애자일 방법론</h3>
<p>애자일 방법론은 유연하고 빠른 개발을 추구하는 방법론이다. </p>
<ul>
<li>반복적 개발: 짧은 개발 주기를 반복하며 점진적으로 소프트웨어를 개선.</li>
<li>고객 협업: 고객의 피드백을 반영하여 지속적으로 개선.</li>
<li>변경 수용: 요구 사항 변경을 적극적으로 수용하고 반영.</li>
<li>작은 팀: 팀원이 소규모로 구성되어 빠른 의사결정과 효율적인 작업 분담이 가능.</li>
</ul>
<hr>
<h3 id="xp">XP</h3>
<p>익스트림 프로그래밍은 애자일 방법론의 일종으로, 품질 높은 소프트웨어를 빠르게 개발하기 위한 방법론이다.</p>
<ul>
<li>고객과의 지속적 소통: 개발 초기부터 끝까지 고객과의 긴밀한 소통.</li>
<li>테스트 우선 개발 (TDD): 기능을 구현하기 전에 테스트 코드를 작성하고, 이를 통과시키며 개발.</li>
<li>작은 배포: 작은 단위로 자주 배포하여 빠르게 피드백을 받음.</li>
<li>페어 프로그래밍: 두 명의 개발자가 하나의 컴퓨터에서 협업하여 코딩을 진행.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 25.02.02 SUN]]></title>
            <link>https://velog.io/@r_louis/TIL-25.02.02-SUN</link>
            <guid>https://velog.io/@r_louis/TIL-25.02.02-SUN</guid>
            <pubDate>Sun, 02 Feb 2025 11:19:04 GMT</pubDate>
            <description><![CDATA[<h3 id="어제-배운-내용-활용">어제 배운 내용 활용</h3>
<p><a href="https://velog.io/@r_louis/TIL-25.02.01-SAT">25.02.01 - TIL</a></p>
<h3 id="메서드-탐색-및-등록">메서드 탐색 및 등록</h3>
<p>진행중인 프로젝트에서 opcode에 따른 패킷 핸들러가 있는데, 하나씩 추가하기보다는 동적으로 opcode에 맞는
메서드를 자동으로 등록시키면 좋을 것 같아 위의 내용을 알아보았다. <code>particial class</code> 라 기본 골자는 다른분이 작성하여 초기화 부분만 작성해봤다.</p>
<pre><code class="language-cs">using UnityEngine;
using System.Reflection;
using System.Linq;
using System;

namespace VampireSurvibal.Net
{
    public partial class PacketHandler
    {

        public void InitializingHandler()
        {
            // opcode와 같은 이름의 private 메서드 자동 초기화

            // private 메서드 읽기
            MethodInfo[] methods = GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);

            // enum에 등록된 이름과 동일한 메서드만 초기화
            foreach (eOpcode opcode in Enum.GetValues(typeof(eOpcode)))
            {
                // opcode명 
                string methodName = opcode.ToString();

                // LINQ로 원하는 메서드 추출
                MethodInfo method = methods.FirstOrDefault(m =&gt; m.Name == methodName 
                                                               &amp;&amp; m.ReturnType == typeof(void) 
                                                               &amp;&amp; m.GetParameters().Length == 1 
                                                               &amp;&amp; m.GetParameters()[0].ParameterType == typeof(Header));

                if (method != null)
                {
                    // 메서드 인포로 델리게이트 생성
                    Action&lt;Header&gt; action = (Action&lt;Header&gt;)Delegate.CreateDelegate(typeof(Action&lt;Header&gt;), this, method);

                    // 핸들러 등록
                    RegisterHandler(opcode, action);
                }
            }
            Debug.Log(&quot;핸들러 초기화 완료&quot;);
        }

        // 예시 메서드
        private void makeRoom(Header rcvHeader)
        {
            // 처리
        }

        private void joinRoom(Header rcvHeader)
        {
            // 처리
        }
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 25.02.01 SAT]]></title>
            <link>https://velog.io/@r_louis/TIL-25.02.01-SAT</link>
            <guid>https://velog.io/@r_louis/TIL-25.02.01-SAT</guid>
            <pubDate>Sun, 02 Feb 2025 11:17:46 GMT</pubDate>
            <description><![CDATA[<h3 id="c-리플렉션">C# 리플렉션</h3>
<p>리플렉션은 프로그램 실행 중 어셈블리, 타입, 메서드 등과 같은 메타데이터를 탐색하고 조작할 수 있는 기능을 제공한다. 리플렉션을 사용하면 타입에 대한 정보 뿐 아니라 객체의 속성, 메서드를 동적 호출 및 변경할 수 있다.</p>
<h4 id="주요-클래스">주요 클래스</h4>
<ul>
<li><p><code>Type</code> : 객체의 타입에 대한 정보를 제공</p>
<pre><code class="language-cs">// example의 Type 객체를 습득.
Type type = typeof(example);</code></pre>
<ul>
<li><code>typeof</code>: 특정 타입의 <code>System.Type</code> 객체를 가져올 수 있다</li>
</ul>
<br></li>
<li><p><code>MethodInfo</code> : 특정 메서드에 대한 메타데이터를 제공</p>
<pre><code class="language-cs">// 타입의 메서드 정보 습득
  MethodInfo methodInfo = type.GetMethod(&quot;MyMethod&quot;);</code></pre>
<ul>
<li><code>BindingFlags</code><ul>
<li>BindingFlags.Instance : 인스턴스 멤버를 검색할 때 사용</li>
<li>BindingFlags.NonPublic : 비공개 멤버(예: <code>private</code> 메서드)를 검색할 때 사용</li>
<li>BindingFlags.Public : 공개 멤버(예: <code>public</code> 메서드)를 검색할 때 사용<br></li>
</ul>
</li>
</ul>
</li>
<li><p><code>GetMethods</code> : 타입 객체 안 메소드를 획득</p>
<pre><code class="language-cs">// 메서드 획득
  MethodInfo[] methods = GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);</code></pre>
</li>
<li><p><code>PropertyInfo</code> : 클래스의 속성 정보를 제공</p>
<pre><code class="language-cs">// 타입의 속성 정보 습득
PropertyInfo propertyInfo = type.GetProperty(&quot;MyProperty&quot;);</code></pre>
</li>
</ul>
<h3 id="델리게이트">델리게이트</h3>
<p>C# 에서 델리게이트는 메서드를 참조할 수 있는 타입으로 메서드를 변수처럼 다룰 수 있게 해주는 객체이다.
이벤트 처리나 콜백 메서드를 구현할 때 매우 유용하게 사용된다.</p>
<h4 id="선언">선언</h4>
<pre><code class="language-cs">public delegate int MyDelegate(int a, int b);</code></pre>
<h4 id="delegatecreatedelegate">Delegate.CreateDelegate</h4>
<ul>
<li><code>Delegate.CreateDelegate</code> : 리플렉션을 사용하여 특정 메서드에 대한 델리게이트를 동적으로 생성할 수 있다. 이를 통해 런타임에 메서드 참조를 동적으로 할당하고 호출할 수 있다.</li>
<li>예시:<pre><code class="language-cs">Action&lt;Header&gt; action = (Action&lt;Header&gt;)Delegate.CreateDelegate(typeof(Action&lt;Header&gt;), this, method);</code></pre>
위 코드는 <code>method</code>에 해당하는 메서드 정보를 기반으로 <code>Action&lt;Header&gt;</code> 타입의 델리게이트를 생성한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 25.01.31 FRI]]></title>
            <link>https://velog.io/@r_louis/TIL-25.01.31-FRI</link>
            <guid>https://velog.io/@r_louis/TIL-25.01.31-FRI</guid>
            <pubDate>Fri, 31 Jan 2025 16:26:38 GMT</pubDate>
            <description><![CDATA[<h3 id="타입스크립트">타입스크립트</h3>
<p>자바스크립트에 타입 시스템을 추가한 언어로 코드의 안정성을 높이기 위해 사용된다. 요즘은 타입스크립트가 추세라고 하여 알아두는 것이 좋을듯하여 살짝 기미해봤다.</p>
<h4 id="1-변수-선언">1. 변수 선언</h4>
<ul>
<li><code>let</code>, <code>const</code>, <code>var</code>를 사용해 변수를 선언할 수 있다. 기본적으로 <code>let</code>과 <code>const</code>를 사용하고, <code>var</code>는 오래된 방식이라 피하는 것이 좋다.</li>
</ul>
<pre><code class="language-typescript">let name: string = &quot;Alice&quot;; // 문자열 타입
const age: number = 30;     // 숫자 타입</code></pre>
<ul>
<li>타입을 명시할 수도 있고, 타입 추론을 통해 자동으로 타입을 추측할 수도 있다.
단, 설정에 따라 사용이 불가하다.</li>
</ul>
<pre><code class="language-typescript">let isActive = true; // boolean 타입으로 추론됨</code></pre>
<h4 id="2-타입-정의">2. 타입 정의</h4>
<ul>
<li>타입스크립트에서는 변수나 함수 파라미터의 타입을 명시적으로 정의할 수 있다.</li>
</ul>
<pre><code class="language-typescript">let score: number = 100;
let isDone: boolean = false;</code></pre>
<h4 id="3-배열과-튜플">3. 배열과 튜플</h4>
<ul>
<li>배열은 타입스크립트에서 <code>Array&lt;타입&gt;</code> 또는 <code>타입[]</code>으로 정의할 수 있다.</li>
</ul>
<pre><code class="language-typescript">let numbers: number[] = [1, 2, 3];    // 숫자 배열
let strings: Array&lt;string&gt; = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]; // 문자열 배열</code></pre>
<ul>
<li>튜플은 고정된 길이와 각 요소의 타입이 다를 수 있는 배열을 정의한다.</li>
</ul>
<pre><code class="language-typescript">let person: [string, number] = [&quot;Alice&quot;, 30];  // 첫 번째 요소는 string, 두 번째는 number</code></pre>
<h4 id="4-함수">4. 함수</h4>
<ul>
<li>함수의 인자와 반환 타입도 정의할 수 있다.</li>
</ul>
<pre><code class="language-typescript">function add(x: number, y: number): number {
  return x + y;
}

const greet = (name: string): string =&gt; `Hello, ${name}`;</code></pre>
<h4 id="5-객체">5. 객체</h4>
<ul>
<li>객체 타입을 정의할 때는 인터페이스(<code>interface</code>)나 타입 별칭(<code>type</code>)을 사용할 수 있다.</li>
</ul>
<pre><code class="language-typescript">interface Person {
  name: string;
  age: number;
}

let user: Person = { name: &quot;Bob&quot;, age: 25 };</code></pre>
<h4 id="6-인터페이스-타입-별칭">6. 인터페이스, 타입 별칭</h4>
<ul>
<li><code>interface</code>는 객체나 클래스의 구조를 정의할 때 사용하고, <code>type</code>은 더 넓은 범위로 사용할 수 있다.</li>
</ul>
<pre><code class="language-typescript">interface Car {
  model: string;
  year: number;
}

type Point = { x: number; y: number };</code></pre>
<h4 id="7-유니온-타입">7. 유니온 타입</h4>
<ul>
<li>유니온 타입은 여러 타입을 하나의 변수에 할당할 수 있게 해준다.</li>
</ul>
<pre><code class="language-typescript">let result: string | number; // string 또는 number
result = &quot;Hello&quot;;
result = 100;</code></pre>
<h4 id="8-null과-undefined">8. Null과 Undefined</h4>
<ul>
<li>타입스크립트에서는 <code>null</code>과 <code>undefined</code>를 명시적으로 처리해야 한다. 기본적으로 <code>null</code>과 <code>undefined</code>는 <code>any</code> 타입을 가질 수 있다.</li>
</ul>
<pre><code class="language-typescript">let notAssigned: null = null;
let uninitialized: undefined = undefined;</code></pre>
<h4 id="9-type-assertion">9. Type Assertion</h4>
<ul>
<li>타입을 강제로 지정할 때 사용한다. 주로 <code>as</code> 키워드를 사용한다.</li>
</ul>
<pre><code class="language-typescript">let someValue: any = &quot;This is a string&quot;;
let strLength: number = (someValue as string).length;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 25.01.30 THU]]></title>
            <link>https://velog.io/@r_louis/TIL-25.01.30-THU</link>
            <guid>https://velog.io/@r_louis/TIL-25.01.30-THU</guid>
            <pubDate>Thu, 30 Jan 2025 15:38:45 GMT</pubDate>
            <description><![CDATA[<h3 id="면접-질문">면접 질문</h3>
<p>아직 면접을 준비하고 있는 것은 아니고 친구와 만드는 어플리케이션이 면접을 대비할 때 사용하는 것이라 테스트용으로 DB를 채우기 위해 몇 개 가져왔다. </p>
<h4 id="1-nodejs란-무엇이며-그-특징은-무엇인가요">1. <strong>Node.js란 무엇이며, 그 특징은 무엇인가요?</strong></h4>
<ul>
<li><strong>답변</strong>: Node.js는 서버 사이드 JavaScript 실행 환경으로, 구글 V8 엔진을 기반으로 작동합니다. 비동기적이고 이벤트 기반의 I/O 모델을 사용하여 높은 성능을 자랑하며, 싱글 스레드로 동작하지만 이벤트 루프와 비동기 처리를 통해 효율적인 처리가 가능합니다. 주로 서버 개발에 사용됩니다.</li>
</ul>
<h4 id="2-nodejs에서-이벤트-루프event-loop란-무엇인가요">2. <strong>Node.js에서 이벤트 루프(Event Loop)란 무엇인가요?</strong></h4>
<ul>
<li><strong>답변</strong>: 이벤트 루프는 Node.js가 비동기 작업을 처리하는 방식으로, 자바스크립트 코드가 실행되는 동안 발생하는 I/O 작업이나 타이머 등을 비동기로 처리하는 역할을 합니다. 이벤트 루프는 호출 스택과 큐를 사용하여 비동기 작업을 차례대로 실행하며, I/O 작업이 완료되면 큐에서 해당 작업을 처리합니다.</li>
</ul>
<h4 id="3-non-blocking-io와-blocking-io의-차이점은-무엇인가요">3. <strong>Non-blocking I/O와 Blocking I/O의 차이점은 무엇인가요?</strong></h4>
<ul>
<li><strong>답변</strong>: Blocking I/O는 작업이 완료될 때까지 다른 작업이 실행되지 않는 방식입니다. 반면, Non-blocking I/O는 I/O 작업이 진행되는 동안 다른 코드가 실행될 수 있도록 하는 방식으로, Node.js는 Non-blocking I/O를 사용해 높은 성능을 발휘합니다. 예를 들어, 파일을 읽는 작업이 비동기로 처리되어 다른 작업을 기다리지 않고 실행될 수 있습니다.</li>
</ul>
<h4 id="4-nodejs의-패키지-관리-도구는-무엇이며-어떻게-사용하나요">4. <strong>Node.js의 패키지 관리 도구는 무엇이며, 어떻게 사용하나요?</strong></h4>
<ul>
<li><strong>답변</strong>: Node.js의 패키지 관리 도구는 <code>npm</code>(Node Package Manager)입니다. <code>npm</code>을 사용해 외부 라이브러리나 패키지를 설치하고 관리할 수 있습니다. 예를 들어, <code>npm install express</code>로 Express 프레임워크를 설치하고, <code>npm init</code>으로 프로젝트의 <code>package.json</code> 파일을 생성합니다. <code>yarn</code>도 대안으로 사용될 수 있습니다.</li>
</ul>
<h4 id="5-require와-import의-차이점은-무엇인가요">5. <strong><code>require()</code>와 <code>import</code>의 차이점은 무엇인가요?</strong></h4>
<ul>
<li><strong>답변</strong>: <code>require()</code>는 CommonJS 모듈 시스템에서 사용되는 함수로, 동기적으로 모듈을 불러옵니다. 반면, <code>import</code>는 ES6 모듈 시스템에서 사용하는 구문으로, 비동기적으로 모듈을 불러옵니다. <code>import</code>는 최신 JavaScript 표준을 따르며, <code>require()</code>는 Node.js에서 주로 사용됩니다.</li>
</ul>
<h4 id="6-nodejs에서-클러스터-모듈을-사용하는-이유와-활용-방법을-설명해주세요">6. <strong>Node.js에서 클러스터 모듈을 사용하는 이유와 활용 방법을 설명해주세요.</strong></h4>
<ul>
<li><strong>답변</strong>: Node.js는 싱글 스레드로 동작하지만, 클러스터 모듈을 사용하면 여러 프로세스를 생성하여 멀티코어 시스템에서 성능을 향상시킬 수 있습니다. 각 프로세스는 독립적인 작업을 수행하며, <code>cluster.fork()</code>를 사용해 워커 프로세스를 생성하고, 이를 통해 병렬 처리가 가능해집니다.</li>
</ul>
<h4 id="7-nodejs에서-callback-hell이란-무엇이며-이를-피하는-방법은-무엇인가요">7. <strong>Node.js에서 <code>callback hell</code>이란 무엇이며, 이를 피하는 방법은 무엇인가요?</strong></h4>
<ul>
<li><strong>답변</strong>: <code>callback hell</code>은 여러 개의 중첩된 콜백 함수들이 코드의 가독성을 떨어뜨리는 현상입니다. 이를 피하기 위해 <code>Promise</code>나 <code>async/await</code>을 사용하여 비동기 코드를 더 읽기 쉽게 작성할 수 있습니다. 예를 들어, <code>async/await</code>는 비동기 코드의 동기적인 흐름을 구현할 수 있어 가독성을 높입니다.</li>
</ul>
<h4 id="8-expressjs란-무엇인가요-그리고-왜-nodejs에서-자주-사용되나요">8. <strong>Express.js란 무엇인가요? 그리고 왜 Node.js에서 자주 사용되나요?</strong></h4>
<ul>
<li><strong>답변</strong>: Express.js는 Node.js를 위한 웹 애플리케이션 프레임워크로, 라우팅, 미들웨어, 요청 및 응답 객체 등의 기능을 제공합니다. RESTful API를 쉽게 설계할 수 있도록 돕고, 빠르게 서버를 구축할 수 있어 Node.js에서 자주 사용됩니다.</li>
</ul>
<h4 id="9-nodejs에서-processnexttick과-setimmediate의-차이점은-무엇인가요">9. <strong>Node.js에서 <code>process.nextTick()</code>과 <code>setImmediate()</code>의 차이점은 무엇인가요?</strong></h4>
<ul>
<li><strong>답변</strong>: <code>process.nextTick()</code>은 현재 실행 중인 이벤트 루프의 작업이 끝난 후, 즉시 콜백을 실행합니다. <code>setImmediate()</code>는 이벤트 루프의 현재 단계가 끝난 후 콜백을 실행합니다. 즉, <code>process.nextTick()</code>은 더 높은 우선순위를 가지며, <code>setImmediate()</code>는 I/O 작업 후 실행됩니다.</li>
</ul>
<h4 id="10-nodejs에서-fs-모듈의-비동기적-파일-시스템-작업을-사용하는-예시를-들어주세요">10. <strong>Node.js에서 <code>fs</code> 모듈의 비동기적 파일 시스템 작업을 사용하는 예시를 들어주세요.</strong></h4>
<ul>
<li><strong>답변</strong>: <code>fs.readFile()</code>은 비동기적으로 파일을 읽는 함수입니다. 예를 들어, <code>fs.readFile(&#39;example.txt&#39;, &#39;utf8&#39;, (err, data) =&gt; { console.log(data); });</code>처럼 파일을 비동기적으로 읽고, 완료 후 콜백을 실행할 수 있습니다. 파일 작업 중 다른 작업을 처리할 수 있도록 비동기 방식을 사용합니다.</li>
</ul>
<h4 id="11-nodejs에서-rest-api를-설계할-때-고려해야-할-점은-무엇인가요">11. <strong>Node.js에서 REST API를 설계할 때 고려해야 할 점은 무엇인가요?</strong></h4>
<ul>
<li><strong>답변</strong>: REST API 설계 시 HTTP 메서드(GET, POST, PUT, DELETE 등)를 적절히 사용하고, 자원의 URI를 명확하고 직관적으로 설계해야 합니다. 또한, 상태 코드(200, 404, 500 등)를 정확하게 사용하고, 요청과 응답의 데이터 포맷을 명확히 정의해야 합니다.</li>
</ul>
<h4 id="12-nodejs에서-보안-취약점이-발생할-수-있는-지점과-이를-예방할-수-있는-방법은-무엇인가요">12. <strong>Node.js에서 보안 취약점이 발생할 수 있는 지점과 이를 예방할 수 있는 방법은 무엇인가요?</strong></h4>
<ul>
<li><strong>답변</strong>: 보안 취약점으로는 SQL 인젝션, XSS, CSRF, 세션 하이재킹 등이 있습니다. 이를 예방하기 위해서는 입력값 검증, 최신 패키지 사용, HTTPS 사용, 세션 보안 강화, CORS 정책 설정 등을 적용해야 합니다.</li>
</ul>
<h4 id="13-nodejs의-스트림stream과-그-종류에-대해-설명해주세요">13. <strong>Node.js의 스트림(Stream)과 그 종류에 대해 설명해주세요.</strong></h4>
<ul>
<li><strong>답변</strong>: Node.js의 스트림은 데이터를 효율적으로 처리할 수 있는 객체입니다. 스트림의 종류는 <code>Readable</code>, <code>Writable</code>, <code>Duplex</code>, <code>Transform</code>이 있으며, 각각 입력과 출력이 가능한 스트림을 다룹니다. 예를 들어, 파일을 읽는 <code>Readable</code> 스트림, 데이터를 보내는 <code>Writable</code> 스트림 등이 있습니다.</li>
</ul>
<h4 id="14-nodejs에서-eventemitter-클래스는-무엇인가요">14. <strong>Node.js에서 <code>EventEmitter</code> 클래스는 무엇인가요?</strong></h4>
<ul>
<li><strong>답변</strong>: <code>EventEmitter</code>는 이벤트 기반 프로그래밍을 위한 클래스로, 객체가 이벤트를 발생시키고 이를 리스닝할 수 있게 도와줍니다. 예를 들어, <code>emitter.on(&#39;event&#39;, callback)</code>을 사용해 이벤트 리스너를 등록하고, <code>emitter.emit(&#39;event&#39;)</code>로 이벤트를 발생시킬 수 있습니다.</li>
</ul>
<h4 id="15-nodejs에서-process-객체의-주요-속성과-사용-방법을-설명해주세요">15. <strong>Node.js에서 <code>process</code> 객체의 주요 속성과 사용 방법을 설명해주세요.</strong></h4>
<ul>
<li><strong>답변</strong>: <code>process</code> 객체는 현재 프로세스의 정보를 담고 있는 객체입니다. <code>process.argv</code>는 명령줄 인자를, <code>process.env</code>는 환경 변수를, <code>process.exit()</code>은 프로세스를 종료하는데 사용됩니다. 예를 들어, <code>process.env.NODE_ENV</code>를 사용하여 환경 변수를 읽어올 수 있습니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 25.01.29 WED]]></title>
            <link>https://velog.io/@r_louis/TIL-25.01.29-WED</link>
            <guid>https://velog.io/@r_louis/TIL-25.01.29-WED</guid>
            <pubDate>Tue, 28 Jan 2025 18:03:58 GMT</pubDate>
            <description><![CDATA[<h3 id="csv-parser">csv-parser</h3>
<p>CSV 파서를 사용하여 행과 열을 읽는 방법에 대하여 알아보았다.<code>csv-parser</code> 라이브러리를 사용하여 CSV 파일에서 데이터를 읽고 파싱하는 방법이다.</p>
<h4 id="csv-parser-라이브러리를-설치"><code>csv-parser</code> 라이브러리를 설치</h4>
<pre><code class="language-bash">npm install csv-parser</code></pre>
<h4 id="기본-틀">기본 틀</h4>
<pre><code class="language-javascript">import fs from &#39;fs&#39;;
import csv from &#39;csv-parser&#39;;

const results = [];

fs.createReadStream(&#39;data.csv&#39;)
  .pipe(csv())
  .on(&#39;data&#39;, (data) =&gt; {
    results.push(data); // 각 행의 데이터를 results 배열에 추가
  })
  .on(&#39;end&#39;, () =&gt; {
    // 데이터 처리
  });</code></pre>
<h4 id="입력-데이터">입력 데이터</h4>
<pre><code class="language-csv">Name,Age,City
Alice,30,New York
Bob,25,Los Angeles
Charlie,35,Chicago</code></pre>
<h4 id="출력-데이터">출력 데이터</h4>
<p>만약 <code>console.log</code>를 찍었다고 하면 다음과 같은 결과값을 띈다.</p>
<pre><code class="language-javascript">[
  { Name: &#39;Alice&#39;, Age: &#39;30&#39;, City: &#39;New York&#39; },
  { Name: &#39;Bob&#39;, Age: &#39;25&#39;, City: &#39;Los Angeles&#39; },
  { Name: &#39;Charlie&#39;, Age: &#39;35&#39;, City: &#39;Chicago&#39; }
]</code></pre>
<h3 id="활용">활용</h3>
<p>일회성 테스트용이라 트랜젝션 처리도 안하고 주저리 주저리 썼다. 대용량 테스트용 데이터 넣기에 좋았다. 
종종 활용할 것 같다.</p>
<pre><code class="language-js">import fs from &#39;fs&#39;;
import path from &#39;path&#39;;
import { fileURLToPath } from &#39;url&#39;;
import csv from &#39;csv-parser&#39;;
import mysql from &#39;mysql2&#39;
import { config } from &#39;../../config/config.js&#39;;

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const csvPath = path.join(__dirname, &#39;./data.csv&#39;);

const { database } = config;

const connection = mysql.createConnection({
  host: database.USER_DB.host,
  user: database.USER_DB.user,
  password: database.USER_DB.password,
  database: database.USER_DB.name,
});

const results = [];

// 파일 읽기
fs.createReadStream(csvPath)
  // csv 스트림 연결
  .pipe(csv())
  .on(&#39;data&#39;, (data) =&gt; {
    // 데이터 입력
    results.push(data);
  })
  .on(&#39;end&#39;, () =&gt; {
    // 데이터 처리
    const filterData = results.filter(
      (result) =&gt; result.answers &amp;&amp; Object.keys(result).length === 9,
    );

    // 문제 SQL
    const problemSql =
      &#39;INSERT INTO problems (sector_id, type, difficulty, title, description, hint, explanation, reference) VALUES ?&#39;;
    // 답안 SQL
    const answerSql = &#39;INSERT INTO options (problem_id, type, option_text, isCorrect) VALUES ?&#39;;

    // 문제 value
    const problemValue = [];
    // 답안 value
    const answerValue = [];

    // Value 처리
    filterData.forEach((data) =&gt; {
      // 문제
      problemValue.push([
        Number(data.sector_id),
        Number(data.typeNum),
        Number(data.difficulty),
        data.title,
        data.description,
        data.hint,
        data.explanation,
        data.reference,
      ]);
    });

    // 데이터베이스에 문제 데이터 삽입
    connection.query(problemSql, [problemValue], (error, results) =&gt; {
      if (error) throw error;

      const insertedId = results.insertId;

      // 삽입된 문제 ID를 기반으로 답안 데이터 설정
      filterData.forEach((data, index) =&gt; {
        const problemId = insertedId + index;
        const answers = data.answers.split(&#39;,&#39;);
        answers.forEach((answer,idx) =&gt; {
          answerValue.push([
            problemId,
            Number(answer.type),
            answer,
            idx===0,
          ]);
        });
      });

      // 데이터베이스에 답안 데이터 삽입
      connection.query(answerSql, [answerValue], (error, results) =&gt; {
        if (error) throw error;
        console.log(&#39;Data inserted successfully.&#39;);
        connection.end();
      });
    });
  });
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 25.01.28 TUE]]></title>
            <link>https://velog.io/@r_louis/TIL-25.01.28-TUE</link>
            <guid>https://velog.io/@r_louis/TIL-25.01.28-TUE</guid>
            <pubDate>Mon, 27 Jan 2025 18:20:32 GMT</pubDate>
            <description><![CDATA[<h3 id="mysql-테이블-동적-업데이트">MYSQL 테이블 동적 업데이트</h3>
<p>어제 TIL에서 테이블에 새로운 컬럼을 추가할 때 <code>NOT NULL</code> 제약에 대응하는 법에 대해 다루었었는데, 현재 사용중인 테이블이고 데이터가 많은 상태라면 동적 업데이트가 중요하다.</p>
<p>수동적인 방법은 다음과 같다.</p>
<pre><code class="language-sql">UPDATE options 
SET type = 2 
WHERE problem_id IN (SELECT problem_id FROM problems WHERE type = 2);</code></pre>
<h4 id="명시적인게-아닌-동적으로-업데이트하고-싶어">명시적인게 아닌 동적으로 업데이트하고 싶어</h4>
<p><code>options</code> 테이블의 <code>type</code>을 <code>problems</code> 테이블에 있는 <code>type</code>으로 업데이트하려면,<code>JOIN</code>을 사용하여 두 테이블을 연결하고, <code>SET</code>을 사용하여 값을 동적으로 설정하면 된다.</p>
<pre><code class="language-sql">UPDATE options o
JOIN problems p ON o.problem_id = p.problem_id
SET o.type = p.type;</code></pre>
<ul>
<li><code>UPDATE options o</code>: <code>options</code> 테이블을 수정한다. 그리고 o라고 부르겠다.</li>
<li><code>JOIN problems p ON o.problem_id = p.problem_id</code>: <code>options</code> 테이블과 <code>problems</code> 테이블을 <code>problem_id</code>를 기준으로 조인하겠다.</li>
<li><code>SET o.type = p.type</code>: <code>options</code> 테이블의 <code>type</code> 값을 <code>problems</code> 테이블에서 읽은 <code>type</code> 값으로 업데이트 하겠다.</li>
</ul>
<h4 id="으음-재밌네-재밌어">으음 재밌네, 재밌어</h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 25.01.27 MON]]></title>
            <link>https://velog.io/@r_louis/TIL-25.01.27-MON</link>
            <guid>https://velog.io/@r_louis/TIL-25.01.27-MON</guid>
            <pubDate>Mon, 27 Jan 2025 16:01:15 GMT</pubDate>
            <description><![CDATA[<h3 id="mysql-컬럼-추가할-때">MYSQL 컬럼 추가할 때</h3>
<p>작업을 하다보니 테이블 변경이 필요했고,<code>NOT NULL</code> 컬럼을 추가할 때, 기존 데이터에 대해서는 기본값을 설정해야 한다. 그렇지 않으면 에러가 발생하기 때문이다.</p>
<h3 id="not-null-컬럼-추가하기">NOT NULL 컬럼 추가하기</h3>
<h4 id="기본-값-추가하여-컬럼-추가">기본 값 추가하여 컬럼 추가</h4>
<p>기존 데이터에 대해서 기본값을 설정하려면 <code>DEFAULT</code> 값을 지정해야 한다. 이렇게 하면 기존 레코드들이 새로운 컬럼을 <code>DEFAULT</code> 값으로 채워지기 때문에 에러 없이 추가할 수 있다.</p>
<pre><code class="language-sql">ALTER TABLE problems
ADD COLUMN type VARCHAR(50) NOT NULL DEFAULT &#39;a&#39;;</code></pre>
<ul>
<li>이렇게 하면 <code>problems</code> 테이블에 새로운 <code>type</code> 컬럼이 추가되고, 기존 데이터의 <code>type</code> 값은 <code>a</code>로 설정된다.</li>
</ul>
<h4 id="기존-데이터에-값을-제공하여-컬럼-추가하기">기존 데이터에 값을 제공하여 컬럼 추가하기</h4>
<p>기존 데이터가 NULL이 될 수 없으므로, 새로운 컬럼에 대한 값을 명시적으로 설정해야 한다. 이 경우, 기존 데이터에 값을 삽입하는 업데이트 쿼리를 작성해야 한다.</p>
<p>예를 들어, <code>problems</code> 테이블에 <code>type</code> 컬럼을 <code>NOT NULL</code>로 추가하려면 먼저 컬럼을 NULL 허용으로 추가하고, 그 다음에 값을 설정한 후에 <code>NOT NULL</code>로 변경할 수 있다.</p>
<pre><code class="language-sql">-- 먼저 NULL 허용 컬럼 추가
ALTER TABLE problems ADD COLUMN type VARCHAR(50);

-- 기존 데이터에 값을 설정
UPDATE problems SET type = &#39;a&#39; WHERE type IS NULL;

-- 그 후, NOT NULL 제약 추가
ALTER TABLE problems MODIFY COLUMN type VARCHAR(50) NOT NULL;</code></pre>
<p>그냥 편하게.. 기본값 추가가 나은 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 25.01.25 SAT]]></title>
            <link>https://velog.io/@r_louis/TIL-25.01.25-SAT</link>
            <guid>https://velog.io/@r_louis/TIL-25.01.25-SAT</guid>
            <pubDate>Sat, 25 Jan 2025 12:47:35 GMT</pubDate>
            <description><![CDATA[<h3 id="mysql-insert-쿼리에서-values--플레이스홀더-사용">MySQL <code>INSERT</code> 쿼리에서 <code>VALUES ?</code> 플레이스홀더 사용</h3>
<h4 id="오랜만에-sql-쉽지-않구만">오랜만에 SQL.. 쉽지 않구만</h4>
<p>MySQL에서 여러 레코드를 한 번에 삽입하려면 <code>INSERT INTO ... VALUES ?</code> 구문을 사용할 수 있다. 이때 <code>?</code> 플레이스홀더는 배열의 배열 형식으로 전달되어야 하는데, 이를 제대로 사용하지 않으면 SQL 구문에서 오류가 발생한다.</p>
<h4 id="오류">오류</h4>
<p>다음과 같은 SQL 구문을 사용했을 때 오류가 발생</p>
<pre><code class="language-js">const queryData = [sector_id, difficulty, title, description, hint, explanation, reference];

// 쿼리 실행
const [rows] = await pools.PROBLEM_DB.query(
  &quot;INSERT INTO problems (sector_id, difficulty, title, description, hint, explanation, reference) VALUES ?&quot;,[queryData]);</code></pre>
<p>에러 메시지:</p>
<pre><code>Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near &#39;3&#39; at line 1</code></pre><h4 id="values는-2중-배열로-전달해야">VALUES는 2중 배열로 전달해야..</h4>
<p><code>VALUES ?</code> 구문은 배열 안에 배열 형태로 값을 전달받는다. 따라서 <code>queryData</code>는 단일 배열로 되어 있었고, 이는 <code>INSERT INTO ... VALUES ?</code>에서 예상하는 형식과 맞지 않았다. <code>queryData</code>를 배열 안에 배열 즉, 2차원 배열로 감싸면 문제를 해결할 수 있다. <code>INSERT INTO ... VALUES ?</code>는 한 번에 여러 개의 레코드를 삽입할 때 사용되므로, 삽입하려는 데이터는 배열의 배열 형식으로 전달되어야 한다.</p>
<h4 id="수정-코드">수정 코드</h4>
<pre><code class="language-js">const queryData = [
  [sector_id, difficulty, title, description, hint, explanation, reference]
];
// 쿼리 실행
const [rows] = await pools.PROBLEM_DB.query(
  &quot;INSERT INTO problems (sector_id, difficulty, title, description, hint, explanation, reference) VALUES ?&quot;, [queryData]);</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 25.01.24 FRI]]></title>
            <link>https://velog.io/@r_louis/TIL-25.01.24-FRI</link>
            <guid>https://velog.io/@r_louis/TIL-25.01.24-FRI</guid>
            <pubDate>Fri, 24 Jan 2025 14:54:42 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/r_louis/post/affa188d-274d-4d77-9897-c381cf8ef16b/image.png" alt=""></p>
<h3 id="gc">GC</h3>
<p>가비지 컬렉터는 자주 언급했던 것 같은데 중요한만큼 한번 더 짚고 가려고 적었다.
C#에서도 메모리 관리는 가비지 컬렉션과 메모리 할당을 통해 이루어지는데 C#은 node와 같이 자동으로 메모리를 관리하지만 개발자가 메모리 관리를 어떻게 최적화 할지에 대한 이해가 중요하다.</p>
<p>가비지 컬렉터는 더 이상 참조되지 않는 객체를 자동으로 메모리에서 해제하여 메모리 누수를 방지한다. 힙 메모리에서 객체를 추적한다.
가비지 컬렉션은 세대 기반으로 동작하는데 이 세대는 0,1,2 세가지로 구분된다.
세대 0은 새로 할당된 객체들이 존재하는 곳으로 자주 가비지 컬렉션이 존재하며 세대 1은 세대 0에서 살아남은 객체들이 위치하는 곳으로 덜 발생한다. 세대 2는 더 오래 살아남은 객체들이 위치하는 곳으로 제일 적게 GC가 발동한다.</p>
<p>Weak Reference - 가비지 컬렉터가 객체를 회수할 수 있도록 하는 참조 방식으로, WeakReference 클래스를 사용하여 참조 객체를 설정할 수 있으며 이는 메모리 누수를 방지하는 데 유용하다.</p>
<p>Finalize - 객체가 가비지 컬렉션되기 전에 호출되는 메서드로, 객체가 파괴될 때 마지막 정리 작업을 수행할 수 있다. 하지만 Finalize는 성능에 영향을 줄 수 있으므로, 가급적 Dispose()를 사용하는 것이 좋다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 25.01.23 THU]]></title>
            <link>https://velog.io/@r_louis/TIL-25.01.23-THU</link>
            <guid>https://velog.io/@r_louis/TIL-25.01.23-THU</guid>
            <pubDate>Thu, 23 Jan 2025 14:29:00 GMT</pubDate>
            <description><![CDATA[<h3 id="c-비동기-및-병렬-처리">C# 비동기 및 병렬 처리</h3>
<p>C#에서 비동기, 병렬 처리하는 방법으로 <code>Task</code>, <code>Parallel</code>, <code>async/await</code> 이 있다.
<code>async/await</code>의 경우 Node.js에서 많이 다뤘기 때문에 익숙하지만 나머지 두 놈은 익숙치 않다.</p>
<h4 id="task">Task</h4>
<p>Task는 비동기 작업을 나타내는 클래스로 C#에서 비동기 작업을 처리하는 주된 방식은 <code>Task</code>를 사용하는 것이다. Task는 비동기 작업을 추상화한 객체로 작업이 완료될 때 결과를 반환하거나 예외를 처리할 수 잇다. 비동기 메서드에서는 <code>Task</code>나 <code>Task&lt;T&gt;</code>를 반환 타입으로 사용한다.</p>
<pre><code class="language-cs">public async Task&lt;string&gt; FetchDataAsync()
{
    // 비동기적으로 데이터를 가져오는 작업 (예: 웹 요청)
    await Task.Delay(2000); // 예시: 2초 지연
    return &quot;데이터 완료!&quot;;
}</code></pre>
<p>Task.WhenAll()을 사용하면 비동기 작업을 병렬적으로 실행하고 모든 작업이 완료되면 결과를 반환한다.
Node.js 에서 <code>Promise</code> 와 <code>Promise.all</code> 과 같은 기능인 것 같다.</p>
<pre><code class="language-cs">public async Task RunMultipleTasksAsync()
{
    Task task1 = Task.Delay(1000); // 1초 지연
    Task task2 = Task.Delay(2000); // 2초 지연
    await Task.WhenAll(task1, task2); // 두 작업이 끝날 때까지 기다림
    Console.WriteLine(&quot;모든 작업 완료&quot;);
}</code></pre>
<h4 id="parallel">Parallel</h4>
<p>Parallel 클래스는 CPU 집약적인 작업을 병렬로 실행하는데 사용되며 여러 프로세서 코어를 활용해 반복문에서 작업을 병렬로 처리하는 데에 적합하다.
여러스레드에서 병렬로 처리하여 성능을 개선할 수 있다. 따라서 수치 계산이나 데이터 처리, 이미지 처리를 하는 등의 작업에서 성능 향상을 기대할 수 있다.</p>
<pre><code class="language-cs">public void ProcessDataInParallel()
{
    // 0부터 9까지 병렬로 작업 처리
    Parallel.For(0, 10, i =&gt;
    {
        Console.WriteLine($&quot;작업 {i} 완료&quot;);
        Task.Delay(500); // 작업 지연
    });
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 25.01.22 WED]]></title>
            <link>https://velog.io/@r_louis/TIL-25.01.22-WED-01dohwfb</link>
            <guid>https://velog.io/@r_louis/TIL-25.01.22-WED-01dohwfb</guid>
            <pubDate>Wed, 22 Jan 2025 14:56:31 GMT</pubDate>
            <description><![CDATA[<p>면접 문제를 제공하는 애플리케이션의 백엔드를 만들던 도중 API 요청을 통해 어느정도를 넘겨줘야 하는지
몰라 찾아보았다.
보통 Stateless API에서 데이터를 조회하는 방법은 주로 페이징 기법을 사용하여 요청한다고 한다.
클라이언트에서 GET요청을 보낼 때 특정 범위의 데이터를 요청할 수 있고 주로 <code>limit</code>, <code>offset</code> 또는
<code>page</code>,<code>per_page</code> 등의 파라미터를 이용하여 전달한다고 한다.</p>
<h4 id="요청-예시">요청 예시</h4>
<pre><code class="language-bash">GET /problems?page=1&amp;per_page=10</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 25.01.21 TUE]]></title>
            <link>https://velog.io/@r_louis/TIL-25.01.21-TUE</link>
            <guid>https://velog.io/@r_louis/TIL-25.01.21-TUE</guid>
            <pubDate>Wed, 22 Jan 2025 14:54:50 GMT</pubDate>
            <description><![CDATA[<h3 id="소프트웨어-공학">소프트웨어 공학</h3>
<p>고품질 소프트웨어를 효율적으로 설계, 개발, 유지보수하기 위한 체계적인 접근 방법으로 이를 통해 소프트웨어 프로젝트의 비용과 시간을 절감하고 품질을 높이는 것이 목표이다</p>
<p>주요 요소로는 다음과 같다.</p>
<ul>
<li>요구사항 분석: 소프트웨어가 해결해야 할 문제를 명확히 정의.</li>
<li>설계: 소프트웨어의 구조와 동작 방식을 구체화.</li>
<li>구현: 설계된 내용을 기반으로 코드를 작성.</li>
<li>테스트: 소프트웨어의 품질을 보장하기 위한 검증.</li>
<li>유지보수: 소프트웨어의 성능 및 안정성을 지속적으로 개선.</li>
</ul>
<h3 id="역공학">역공학</h3>
<p>기존 시스템이나 소프트웨어의 구조, 기능, 동작 원리를 분석하여 문서화하거나 복제하는 과정으로 주로 다음의 목적을 위해 사용된다.</p>
<ul>
<li>시스템 분석: 기존 소프트웨어를 이해하여 수정하거나 통합하기 위해.</li>
<li>호환성 확보: 레거시 시스템과의 호환성을 보장하기 위해.</li>
<li>보안 강화: 취약점을 분석하여 보완책 마련.</li>
<li>교육 및 학습: 소프트웨어 작동 원리를 학습.</li>
</ul>
<h3 id="소프트웨어-개발-방법론">소프트웨어 개발 방법론</h3>
<p>프로젝트 관리 및 개발 과정에서 사용할 체계적인 절차와 원칙을 정의한 것으로 주요 방법론은 폭포수, 나선형 등이 있다.</p>
<h4 id="폭포수-모형">폭포수 모형</h4>
<p>선형 순차적 모델이라고도 하며 Boehm이 제시한 고전적 생명주기 모델로 소프트웨어 개발 과정의 각 단계가 순차적으로 진행되는 모형이다.</p>
<h4 id="나선형-모형">나선형 모형</h4>
<p>반복적인 작업을 수행하는 점증적 생명주기 모형으로 위험을 관리하고 최소화하는 것이 목적으로 나선을 따라 돌어가며 각 개발 순서를 반복하여 수행하는 점진적 방식으로 누락된 요구사항을 추가할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 25.01.20 MON]]></title>
            <link>https://velog.io/@r_louis/TIL-25.01.20-MON</link>
            <guid>https://velog.io/@r_louis/TIL-25.01.20-MON</guid>
            <pubDate>Mon, 20 Jan 2025 10:41:44 GMT</pubDate>
            <description><![CDATA[<h3 id="express-validator">express-validator</h3>
<p>api 서버를 개발하는 도중 미들웨어에서 파라미터를 검증하면 좋을 것 같아 찾아보다가 <code>express-validator</code> 라는 라이브러리를 찾았다.
해당 라이브러리는 Express 애플리케이션에서 HTTP 요청을 처리할 때 바디, 쿼리, 헤더, 경로 등 포함된 데이터 유효성 검사를 쉽게할 수 있게 해주는 미들웨어 라이브러리로 유효하지 않은 데이터가 들어올 때 에러 메세지를 반환할 수 있다.</p>
<h4 id="주요-기능">주요 기능</h4>
<ul>
<li><p>유효성 검사
isEmail(): 이메일 형식 검사
isInt(): 정수형 검사
isLength(): 길이 검사
isAlpha(): 알파벳만 검사
isNumeric(): 숫자만 검사
isAlphanumeric(): 알파벳과 숫자만 검사
isIn(): 특정 값이 목록에 포함되어 있는지 검사</p>
<ul>
<li><p>예시</p>
<p>body(&#39;username&#39;).isLength({ min: 3, max: 20 }).withMessage(&#39;Username must be between     3 and 20 characters&#39;)</p>
</li>
</ul>
</li>
<li><p>가공
trim(): 앞뒤 공백 제거
escape(): HTML 특수 문자를 이스케이프 처리
toLowerCase(): 소문자로 변환
toInt(): 문자열을 정수로 변환</p>
<ul>
<li><p>예시</p>
<p>body(&#39;email&#39;).normalizeEmail()  // 이메일 주소의 대소문자를 정규화</p>
</li>
</ul>
</li>
</ul>
<h3 id="적용">적용</h3>
<p>다른 좋은 방안도 많겠지만 일단 객체로 관리해서 미들웨어로 가져다 쓰는 방식을 생각해봤다.
나중에도 활용을 할 지 보고 이불킥을 할 지는 모르지만 일단 써보자.</p>
<h4 id="params-미들웨어">params 미들웨어</h4>
<pre><code class="language-js">import { body, validationResult } from &#39;express-validator&#39;;
const errHandler = (req, res, next) =&gt; {
  const err = validationResult(req);
  if (!err.isEmpty()) {
    return res.status(400).json({
      statusCode: 400,
      message: &#39;Parameter validation failed. You can find the reason at &quot;errors&quot;&#39;,
      errors: err.array(),
    });
  }
  next();
};

export const paramsValidator = {
  auth: {
    oAuthLogin: [body(&#39;code&#39;).notEmpty().withMessage(&#39;Not found parameter &quot;code&quot;&#39;), errHandler],
  },
  problems: {
    createProblem: [
      body(&#39;sector&#39;).notEmpty().withMessage(&#39;Not found parameter &quot;sector&quot;&#39;),
      body(&#39;difficulty&#39;).notEmpty().withMessage(&#39;Not found parameter &quot;difficulty&quot;&#39;),
      body(&#39;title&#39;).notEmpty().withMessage(&#39;Not found parameter &quot;title&quot;&#39;),
      body(&#39;description&#39;).notEmpty().withMessage(&#39;Not found parameter &quot;description&quot;&#39;),
      body(&#39;answer&#39;).notEmpty().withMessage(&#39;Not found parameter &quot;answer&quot;&#39;),
      body(&#39;hint&#39;).notEmpty().withMessage(&#39;Not found parameter &quot;hint&quot;&#39;),
      body(&#39;explanation&#39;).notEmpty().withMessage(&#39;Not found parameter &quot;explanation&quot;&#39;),
      body(&#39;reference&#39;).notEmpty().withMessage(&#39;Not found parameter &quot;reference&quot;&#39;),
      errHandler,
    ],
  },
};</code></pre>
<h4 id="router-적용-예시">router 적용 예시</h4>
<pre><code class="language-js">router.post(&#39;/git/token&#39;, paramsValidator.auth.oAuthLogin, authController.oAuthLogin);</code></pre>
<p><img src="https://velog.velcdn.com/images/r_louis/post/a77be9e2-9374-4060-9d36-d285e56ff3cd/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 25.01.17 FRI - 25.01.19 SUN]]></title>
            <link>https://velog.io/@r_louis/TIL-25.01.17-FRI-25.01.19-SUN</link>
            <guid>https://velog.io/@r_louis/TIL-25.01.17-FRI-25.01.19-SUN</guid>
            <pubDate>Sun, 19 Jan 2025 13:29:10 GMT</pubDate>
            <description><![CDATA[<p>여행으로 인한 TIL 휴식
화천 산천어 축제 다녀왔는데 일반 산천어 2배크기 잡아서 기분이 매우 좋은 상태
회 떠주시는 분들도 무슨 민어를 잡아왔냐고 ㅋㅋㅋ</p>
<p><img src="https://velog.velcdn.com/images/r_louis/post/f1e491ef-85ee-4656-ac77-f1f9d949a425/image.jpg" alt=""></p>
<p>한 4~50 CM는 됐던 것 같다.</p>
<p>내일부터 다시 마음 잡고 공부 스타트 해보자.</p>
]]></description>
        </item>
    </channel>
</rss>