<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>kt.gmLOG</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sat, 28 Feb 2026 17:34:10 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>kt.gmLOG</title>
            <url>https://velog.velcdn.com/images/kt_gml/profile/f59a162a-3422-477b-9dbc-385e05305dc8/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. kt.gmLOG. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/kt_gml" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[2025년 정산 - SQLD 취득 / JSP 프로젝트 / 경진대회 수상 / 토익 편입 합격]]></title>
            <link>https://velog.io/@kt_gml/2025%EB%85%84-%EC%A0%95%EC%82%B0-SQLD-%EC%B7%A8%EB%93%9D%EA%B3%BC-%EC%88%98%EC%83%81-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%ED%86%A0%EC%9D%B5-%ED%8E%B8%EC%9E%85-4%EA%B4%80%EC%99%95</link>
            <guid>https://velog.io/@kt_gml/2025%EB%85%84-%EC%A0%95%EC%82%B0-SQLD-%EC%B7%A8%EB%93%9D%EA%B3%BC-%EC%88%98%EC%83%81-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%ED%86%A0%EC%9D%B5-%ED%8E%B8%EC%9E%85-4%EA%B4%80%EC%99%95</guid>
            <pubDate>Sat, 28 Feb 2026 17:34:10 GMT</pubDate>
            <description><![CDATA[<h1 id="2025년-정산">2025년 정산</h1>
<p>무휴학 편입 특성상 편입 직전의 집중도 있는 학습과 연속적인 편입 시험으로 너무 바빠서 한동안 벨로그를 작성하지 못했다.</p>
<p>그래서 지난 기간동안 어떤 성과를 냈는지 정리하고 넘어가려고 한다.</p>
<p>길고 길었던 편입 준비가 좋은 결과로 끝났고 새로운 학교에서는 백엔드와 더 깊이있는 코딩테스트 위주로 학습하면서 전공 공부에 매진할 생각이다. </p>
<p>앞으로는 배운 개념이나 이론 위주의 블로그 보다는 프로젝트나 학습하면서 느낀점들 위주로 작성해보려고 한다.</p>
<hr>
<h2 id="sqld-취득">SQLD 취득</h2>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/8c95e06d-36b5-45a5-97b0-d32bce65d9ad/image.png" alt=""></p>
<p>바쁜 와중에 <code>데이터베이스설계</code>와 <code>데이터관리</code> 수업을 듣고 SQL에 입문한 뒤 일주일 빠짝 공부해서 SQLD 취득에 성공했다.</p>
<p>어짜피 시험 범위였기에 미리 자격증을 따고 수월하게 A+를 받았다.</p>
<hr>
<h2 id="체육대전-농구-우승">체육대전 농구 우승</h2>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/bbe923c7-faa2-4a85-8100-563484f6b069/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/ce589da7-88e6-4aa2-8560-3acb3c913c8a/image.png" alt=""></p>
<p>경영학부의 에이스 선배들 덕분에 많이 뛰지도 않고 우승 트로피를 만졌다.</p>
<p>기여도 4%..? 경기 안뛸때는 벤치에서 응원 열심히 했다.</p>
<p>정작 열심히 한 배구는 예선 탈락했다는 사실..</p>
<hr>
<h2 id="교내-sw경진대회-장려상-수상">교내 SW경진대회 장려상 수상</h2>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/346770c5-73e7-4d7c-ad1c-3e63ab283a56/image.png" alt=""></p>
<p>Python, C++ 라이브러리를 활용하는 고학년들의 경쟁속이었다.</p>
<p>C랑 자료구조 알고리즘 공부하면서 백준 골드를 달성한 김에 C언어로 용감하게 대회에 신청하였다.</p>
<p>C언어로 큐를 직접 구현해가면서 문제를 푼 사람은 나밖에 없었던 것 같다.</p>
<p>어짜피 편입 시험 범위니까 공부한다는 생각으로 출전했는데 필요한 자료구조를 직접 구현하며 절반 정도 풀고나니 시간이 부족해서 더 풀 수가 없었다.</p>
<p>그래도 장려상이라도 받았으니 만족스럽다. </p>
<p>경북대학교에서는 Java로 전향하여 백준 플레티넘과 각종 알고리즘 대회 수상을 목표로 나아가려고한다.</p>
<hr>
<h2 id="시흥실록지리지로컬-창업-경진대회-장려상-수상">시흥실록지리지(로컬 창업 경진대회) 장려상 수상</h2>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/346fb4a4-08b8-455a-a5c5-478a4e4a45c2/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/6c8ecbc2-c7ad-4029-8f24-41913d30096f/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/9d300231-1bf8-4ec5-b3ae-4ad938eb19ce/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/2fa9fab8-062a-45f1-8993-77d6a8e9af29/image.png" alt=""></p>
<p>정말 많은 시간을 할애했지만 아쉽게도 가장 낮은 상을 수상하였다.</p>
<p>한국공학대학교라 그런건지 공학 관련 창업 아이템들이 상을 휩쓸었고 바이럴 마케팅이 메인 주제였던 우리의 서비스는 열띤 발표에도 불구하고 교수님들을 끝내 설득하지 못했다.</p>
<p>그래도 담당 공무원과 인터뷰를 잡고 직접 진행해보고, 각종 전문가들에게 문의하면서 견적내고, 각종 활동에서 여러 멘토님들도 만나면서 인사이트가 많이 넓어진것 같다. </p>
<p>또한 최고급 호텔 스위트룸에서 밤새 프로젝트에 몰두해보기도 하고, 창업 전문가들, 교수님들, 기자들, 수많은 학우들 앞에서 여러 차례 발표해볼 수 있는 좋은 경험이었다.</p>
<hr>
<h2 id="각종-대회-출전-경험">각종 대회 출전 경험</h2>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/fb1d2a0e-0090-48d0-8491-1bfe6b36feec/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/d5a45d94-3d18-405b-99b0-b23d98d29d7e/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/082874b0-3bdd-4d7c-8a2c-a66a140f2e89/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/cb6c199c-0551-4c95-8648-03396bf61eb6/image.png" alt=""></p>
<p>창업 경진대회에 조원으로 참여하기도 하였고, 제주 공공데이터 활용 공모전에도 과감하게 출전하였으나 예선 탈락하였다.</p>
<hr>
<h2 id="jsp-쇼핑몰-프로젝트">JSP 쇼핑몰 프로젝트</h2>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/2777f37d-8ed1-4c53-bc6e-356fe26cbe13/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/f6aa2485-d37b-43ff-89a8-0dc8315fe1a9/image.png" alt=""></p>
<p><a href="https://github.com/TaeHuiKKIM/MERCI-JSP">GITHUB - MERCI JSP</a></p>
<p>JSP로 개발한 반응형 쇼핑몰 웹사이트 프로젝트이다.</p>
<p>상업적인 목적은 없으며 데이터베이스 연동 학습 목적으로 개발하였다.</p>
<p><a href="https://wellbeingexpress.com">https://wellbeingexpress.com</a></p>
<p>위 사이트의 메인 페이지 디자인을 참고하였으며 의류 이미지를 샘플 데이터로 사용하였다.</p>
<blockquote>
<p>*<em>구현한 기능 *</em>
물품 관리 - 등록/수정/삭제 - 품목 별 리뷰
고객 관리 - 회원가입 / 계정 삭제 / 로그인(소셜) 및 비밀번호 변경 - 찾기 / 상품 찜 기능 / 주소지 저장 및 불러오기 (도로명주소 API)
주문 관리 - 결제 확인 및 주문 완료 변경 / 리뷰 등록-수정-삭제
주문 및 결제(장바구니 및 대행사 활용) - AJAX 활용하여 사용 편의성 향상
문의 관리 - QNA 등록 수정 삭제
물품 카테고리별 나열 및 검색
소개 페이지 및 수정 페이지</p>
</blockquote>
<p>이외에도 디테일적인 부분들이 많고 결제는 대행사 테스트용 API를 활용했다.</p>
<hr>
<h2 id="2학년-수료-및-편입-시험-준비">2학년 수료 및 편입 시험 준비</h2>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/c811170e-a844-43cb-a6f3-2bacba13a229/image.png" alt=""></p>
<p>평균 평점 <strong>4.41</strong>로 <strong>한국공학대학교</strong> 2학년을 수료하였고 방학 동안은 편입 시험 공부에 집중하였다.</p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/389a9e17-208d-448e-920b-f4133129c6c3/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/7eb00f03-0831-4c88-94e8-c13a9b82aac8/image.png" alt=""></p>
<p><strong>레드 블랙 트리</strong>나 <strong>AVL 트리</strong>의 삭제 등 심화적인 부분까지 대비하였다. </p>
<p>처음엔 이해가 안갔는데 구현해본 코드를 외우고 설명할 정도까지 학습하고 나니까 이젠 쉽게 느껴진다.</p>
<p><strong>C++</strong>도 학습하며 STL 라이브러리의 활용과 각종 자료구조를 사용하는 코드를 구현하는걸 타이핑이 아니라 연필로 써가며 무한 반복하였다.</p>
<p>*<em>이산수학 / C / C++ / 자료구조 *</em>등 출제범위가 광범위하기에 내가 아무리 공부해도 모르는게 나와버리면 어떡하지라는 불안감속에서 끊임없이 반복학습하였다.</p>
<p>시험 직전에는 더 이상 공부할게 없었다.</p>
<p>이렇게까지 했는데 내가 못 풀면 남들도 못 푸는 문제다 라는 생각이 들 정도로 대비되어있었다. </p>
<p>다행히 큰 이변 없이 각종 면접과 시험에서 좋은 결과를 냈고 토익 편입으로 갈수 있는 모든 학교에 합격하였다.</p>
<p>그중 IT 분야 강자인 경북대학교로 진학하게 되었다.</p>
<hr>
<h2 id="앞으로는-">앞으로는 ?</h2>
<p>Java로 코딩테스트를 학습하면서 객체지향언어에 익숙해질것이다.</p>
<p>동시에 Spring을 깊이있게 학습해 대기업 백엔드 직군에 적합한 인재가 되어보려고 한다.</p>
<p>이 과정에서 바이브 코딩이나 openclaw 등을 활용하며 나의 깊은 지식과 AI의 생산성을 융합시켜 각종 대회에도 진출할 생각이다.</p>
<p>무엇보다도 동아리나 대외활동에 적극 참여하며 같은 분야에 속한 열정적인 학우들과 함께 성장하고싶다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[UNIX] GDB(GNU Debugger) 사용법]]></title>
            <link>https://velog.io/@kt_gml/UNIX-GDBGNU-Debugger-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@kt_gml/UNIX-GDBGNU-Debugger-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Sat, 11 Oct 2025 13:52:47 GMT</pubDate>
            <description><![CDATA[<h1 id="gdbgnu-debugger-사용법">GDB(GNU Debugger) 사용법</h1>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/805e2085-51ee-4b13-89e8-674bcfad7e74/image.png" alt=""></p>
<p>코딩테스트 문제를 풀때 디버거를 잘 몰라서 printf와 같은 출력문을 반복문 사이사이에 넣어서 풀곤 했었다.</p>
<p>하지만 UNIX를 배우면서 <code>Make Utility</code>와 <code>GCC 컴파일러</code>에 대해 배우면서 <code>GDB</code>에 대해 배울 수 있었다.</p>
<p>이를 배우면서 흥미를 느껴 이에 대해 기록하고 앞으론 IDE에서도 디버거를 활용하려고한다.</p>
<hr>
<h2 id="디버거란-">디버거란 ?</h2>
<p><code>printf</code>를 코드 곳곳에 삽입하여 변수 값을 확인하는 방식은 간단하지만, 재귀와 같은 알고리즘이 쓰이면서 점점 복잡해지는 프로그램에서는 한계에 부딪힌다. </p>
<p>코드는 지저분해지고, 버그를 잡은 뒤에는 모든 <code>printf</code>를 다시 지워야 하는 번거로움도 존재한다.</p>
<p><code>GDB(GNU Debugger)</code>는 <code>리눅스(유닉스)</code> 환경에서 이런 원시적인 방법을 대체하는 강력한 디버깅 도구이다. </p>
<p>프로그램을 특정 지점에서 멈추게 하고, 변수 값을 실시간으로 들여다보며, 코드를 한 줄씩 실행하는 등 프로그램의 내부를 정밀하게 분석할 수 있게 해준다.</p>
<hr>
<h2 id="디버깅-정보-포함하여-컴파일하기">디버깅 정보 포함하여 컴파일하기</h2>
<p>GDB가 소스 코드의 내용을 제대로 인식하고 분석하려면, 컴파일할 때 <strong>디버깅 정보</strong>를 포함시켜야 한다. GCC 컴파일러에서 <code>-g</code> 옵션을 사용하면 된다.</p>
<pre><code class="language-bash"># -g 옵션을 추가하여 컴파일한다.
gcc -g -o factorial factorial.c</code></pre>
<p><code>-g</code> 옵션 없이 컴파일된 실행 파일은 GDB로 분석할 수는 있지만, 소스 코드의 몇 번째 줄인지 추적하거나 변수명을 확인하는 등의 핵심 기능을 제대로 사용할 수 없다. </p>
<p><strong>GDB</strong>를 사용하려면 <code>-g</code> 옵션을 꼭 사용해야한다.</p>
<hr>
<h2 id="버그가-있는-팩토리얼-프로그램">버그가 있는 팩토리얼 프로그램</h2>
<p>아래는 5의 팩토리얼(!5)을 계산하는 코드이지만 버그를 포함하고 있다. </p>
<p>5!는 120이어야 하지만, 이 프로그램은 잘못된 결과를 출력한다.</p>
<pre><code class="language-c">#include &lt;stdio.h&gt;

// n! (n 팩토리얼)을 계산하는 함수
int calculate_factorial(int n) {
    int result = 1;

    // 버그: i &lt;= n 이 되어야 하지만 i &lt; n 으로 되어있다.
    for (int i = 1; i &lt; n; i++) {
        result = result * i;
    }
    return result;
}

int main(void) {
    int number = 5;
    int result = 0;

    result = calculate_factorial(number);

    printf(&quot;%d의 팩토리얼 결과: %d\n&quot;, number, result);

    return 0;
}</code></pre>
<p>이 코드를 <code>-g</code> 옵션으로 컴파일하고 실행하면 <code>5의 팩토리얼 결과: 24</code>라는 오답이 나온다.</p>
<hr>
<h2 id="단계별-디버깅-과정">단계별 디버깅 과정</h2>
<p><strong>1) GDB 시작</strong>
터미널에 <code>gdb</code> 명령어와 함께 분석할 실행 파일명을 입력한다.</p>
<pre><code class="language-bash">gdb ./factorial</code></pre>
<p>GDB가 시작되면 <code>(gdb)</code> 프롬프트가 나타나며 명령어 입력을 기다린다.</p>
<p><strong>2) 브레이크포인트(Breakpoint) 설정</strong>
<strong>브레이크포인트</strong>는 프로그램 실행 중 의도적으로 멈추고 싶은 지점을 설정하는 것이다. </p>
<p>가장 먼저 <code>main</code> 함수가 시작되는 지점에 설정해보자.</p>
<pre><code class="language-gdb">(gdb) break main
Breakpoint 1 at 0x1169: file factorial.c, line 15.</code></pre>
<p><code>break [함수명]</code> 또는 <code>b [줄 번호]</code> 형식으로 설정할 수 있다.</p>
<p><strong>3) 프로그램 실행</strong>
<code>run</code> (축약형 <code>r</code>) 명령어로 프로그램을 실행한다. </p>
<p>프로그램은 실행되다가 이전에 설정한 브레이크포인트(<code>main</code> 함수)를 만나면 즉시 멈춘다.</p>
<pre><code class="language-gdb">(gdb) run
Starting program: /path/to/factorial

Breakpoint 1, main () at factorial.c:15
15        int number = 5;</code></pre>
<p>이제 프로그램은 15번째 줄이 실행되기 직전에 멈춰있다.</p>
<p><strong>4) 코드 한 줄씩 실행 : <code>next</code></strong>
<code>next</code> (축약형 <code>n</code>) 명령어는 현재 줄을 실행하고 바로 다음 줄에서 멈춘다. </p>
<p><code>main</code> 함수의 코드를 한 줄씩 따라가 보겠다.</p>
<pre><code class="language-gdb">(gdb) next
16        int result = 0;
(gdb) next
18        result = calculate_factorial(number);</code></pre>
<p><strong>5) 변수 값 확인 : <code>print</code></strong>
<code>print</code> (축약형 <code>p</code>) 명령어는 현재 시점의 변수 값을 보여준다.</p>
<pre><code class="language-gdb">(gdb) print number
$1 = 5
(gdb) print result
$2 = 0</code></pre>
<p><code>number</code> 변수에는 5가, <code>result</code> 변수에는 0이 올바르게 들어가 있음을 확인했다.</p>
<p><strong>6) 함수 내부로 들어가기: <code>step</code></strong>
다음 실행할 18번 라인은 <code>calculate_factorial</code> 함수 호출이다. </p>
<p>이 함수의 내부 동작을 보고 싶다면 <code>next</code> 대신 <code>step</code> (축약형 <code>s</code>)을 사용한다. </p>
<p><code>step</code>은 함수 호출을 만나면 그 함수 내부로 진입한다.</p>
<pre><code class="language-gdb">(gdb) step
calculate_factorial (n=5) at factorial.c:5
5        int result = 1;</code></pre>
<p><code>calculate_factorial</code> 함수 내부로 들어왔고, 파라미터 <code>n</code>에 5가 잘 전달된 것을 볼 수 있다.</p>
<p><strong>7) 버그 추적</strong>
이제 <code>for</code>문 안에서 변수들이 어떻게 변하는지 <code>next</code>와 <code>print</code>로 추적해보자.</p>
<pre><code class="language-gdb">(gdb) next
8        for (int i = 1; i &lt; n; i++) {
(gdb) next
9            result = result * i;
(gdb) print i
$3 = 1
(gdb) print result
$4 = 1
````next`를 반복하며 `i`와 `result`의 변화를 계속 관찰한다.

```gdb
# ... next를 계속 입력 ...

(gdb) next
9            result = result * i;
(gdb) p i
$7 = 4
(gdb) p result
$8 = 6
(gdb) next
8        for (int i = 1; i &lt; n; i++) {
(gdb) p result
$9 = 24
(gdb) next
11        return result;</code></pre>
<p><code>i</code>가 4일 때 <code>result</code>는 <code>6 * 4 = 24</code>가 되었다. </p>
<p>그리고 <code>i</code>가 5가 되자 <code>i &lt; n</code> (즉, <code>5 &lt; 5</code>) 조건이 거짓이 되어 루프를 빠져나왔다.</p>
<p>여기서 팩토리얼은 5까지 곱해야 하는데, <code>i</code>가 4일 때까지만 곱하고 루프가 종료되었음을 확인할 수 있다. </p>
<p><code>for</code>문의 조건이 <code>i &lt; n</code>이 아니라 <code>i &lt;= n</code>이 되어야 한다.</p>
<p><strong>8) 디버깅 종료</strong>
원인을 찾았으니 디버깅을 종료한다. <code>quit</code> (축약형 <code>q</code>) 명령어를 입력한다.</p>
<pre><code class="language-gdb">(gdb) quit
A debugging session is active.

    Inferior 1 [process 12345] will be killed.

Quit anyway? (y or n) y</code></pre>
<hr>
<h2 id="gdb-핵심-명령어-정리">GDB 핵심 명령어 정리</h2>
<table>
<thead>
<tr>
<th align="left">명령어</th>
<th align="left">축약형</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>break</code></td>
<td align="left"><code>b</code></td>
<td align="left">브레이크포인트를 설정한다. (예: <code>b main</code>, <code>b 12</code>)</td>
</tr>
<tr>
<td align="left"><code>run</code></td>
<td align="left"><code>r</code></td>
<td align="left">프로그램을 실행한다.</td>
</tr>
<tr>
<td align="left"><code>next</code></td>
<td align="left"><code>n</code></td>
<td align="left">다음 줄로 이동한다. (함수 안으로 들어가지 않음)</td>
</tr>
<tr>
<td align="left"><code>step</code></td>
<td align="left"><code>s</code></td>
<td align="left">다음 줄로 이동한다. (함수 안으로 들어감)</td>
</tr>
<tr>
<td align="left"><code>print</code></td>
<td align="left"><code>p</code></td>
<td align="left">변수의 값을 출력한다. (예: <code>p my_variable</code>)</td>
</tr>
<tr>
<td align="left"><code>list</code></td>
<td align="left"><code>l</code></td>
<td align="left">현재 위치 주변의 소스 코드를 보여준다.</td>
</tr>
<tr>
<td align="left"><code>continue</code></td>
<td align="left"><code>c</code></td>
<td align="left">다음 브레이크포인트까지 실행을 계속한다.</td>
</tr>
<tr>
<td align="left"><code>quit</code></td>
<td align="left"><code>q</code></td>
<td align="left">GDB를 종료한다.</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unix] Vi 편집기와 GCC 사용법]]></title>
            <link>https://velog.io/@kt_gml/UnixVim-Vi-%ED%8E%B8%EC%A7%91%EA%B8%B0%EC%99%80-GCC-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@kt_gml/UnixVim-Vi-%ED%8E%B8%EC%A7%91%EA%B8%B0%EC%99%80-GCC-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Sat, 11 Oct 2025 11:33:56 GMT</pubDate>
            <description><![CDATA[<h1 id="unix에서의-vi-편집기와-gcc-사용법">Unix에서의 Vi 편집기와 GCC 사용법</h1>
<p>Windows의 Visual Studio 같은 통합 개발 환경(IDE)은 매우 편리하지만, 많은 개발 환경, 특히 서버는 그래픽 인터페이스(GUI)가 없는 터미널 기반의 리눅스로 운영된다. </p>
<p>따라서 터미널 환경에서 코드를 작성하고 컴파일하는 능력은 개발자의 기본 소양이다.</p>
<p>이번에 강의에서 UNIX를 다루게 되면서 처음엔 복사 붙여넣기도 제대로 못하고 Vim 모드에서 벗어나기도 힘들었다.</p>
<p>이처럼 윈도우에 익숙해진 나에게 어려운 점이 많아서 오래 기억하고 두고두고 볼 수 있도록 글을 써보려고한다.</p>
<h2 id="vimvi-편집기-사용법">Vim(vi) 편집기 사용법</h2>
<p>Vim은 키보드만으로 모든 작업을 수행하는 터미널 기반의 강력한 텍스트 편집기이다. </p>
<p>최초의 유닉스용 화면 편집기이고 모든 유닉스/리눅스 시스템이 기본적으로 갖추고 있다.</p>
<p>Vim의 가장 큰 특징은 <strong>&#39;모드(Mode)&#39;</strong> 라는 개념이 있다는 것이다.</p>
<h3 id="vim의-3가지-모드">Vim의 3가지 모드</h3>
<p>Vim을 효과적으로 사용하려면 아래 3가지 모드를 이해해야 한다.</p>
<ol>
<li><p><strong>명령 모드 (Normal Mode)</strong>
Vim을 실행했을 때의 기본 상태이다.
키를 입력하면 텍스트가 써지는 것이 아니라, 커서 이동, 내용 복사/붙여넣기, 삭제 등의 <strong>명령</strong>이 실행된다.</p>
</li>
<li><p><strong>입력 모드 (Insert Mode)</strong>
실제로 텍스트를 입력하고 코드를 작성할 수 있는 모드이다.
명령 모드에서 <code>i</code>, <code>a</code>, <code>o</code> 등의 키를 눌러 진입할 수 있다.
이 모드에서 나가려면 <code>ESC</code> 키를 누르면 된다.</p>
</li>
<li><p><strong>마지막 행 모드 (Command-Line Mode)</strong>
명령 모드에서 <code>:</code>(콜론) 키를 눌러 진입한다.
화면 맨 아래 줄에 명령어를 입력하여 파일 저장, 종료, 검색 등 강력한 기능을 수행한다.</p>
</li>
</ol>
<hr>
<h3 id="vim으로-helloc-파일-작성하기">Vim으로 <code>hello.c</code> 파일 작성하기</h3>
<p><strong>1) Vim 실행 및 파일 생성</strong>
<code>vim</code>혹은 <code>vi</code> 명령어 뒤에 생성할 파일명을 입력한다. 파일이 존재하면 열리고, 없으면 새로 생성된다.</p>
<pre><code class="language-bash">vi hello.c</code></pre>
<p>실행하면 비어있는 화면이 나타나며, 현재는 <strong>명령 모드</strong> 상태이다.</p>
<p><strong>2) 입력 모드로 전환</strong>
코드를 작성하기 위해 키보드에서 <code>i</code> 키를 누른다. 화면 하단에 <strong><code>-- 끼워넣기 --</code></strong> 혹은 <strong><code>-- INSERT --</code></strong> 라는 문구가 나타나면 입력 모드로 전환된 것이다.</p>
<p><strong>3) 코드 작성</strong>
이제 자유롭게 C 코드를 작성할 수 있다.</p>
<pre><code class="language-c">#include &lt;stdio.h&gt;

int main(void)
{
    printf(&quot;Hello, World!! \n&quot;);
    return 0;
}</code></pre>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/8d40a521-84fd-43a1-8540-be636b8dcf8d/image.png" alt=""></p>
<p><strong>4) 저장 및 종료</strong>
코드 작성을 마쳤다면, 파일을 저장하고 Vim을 종료해야 한다.</p>
<ol>
<li><p><code>ESC</code> 키를 눌러 <strong>명령 모드</strong>로 돌아간다. (<code>-- INSERT --</code> 문구가 사라진다.)</p>
</li>
<li><p><code>:</code>(콜론)을 입력하여 <strong>마지막 행 모드</strong>로 전환한다.</p>
</li>
<li><p>원하는 명령어를 입력하고 <code>Enter</code>를 누른다.</p>
<p><code>:w</code> : (Write) 현재 파일을 <strong>저장</strong>한다.
<code>:q</code> : (Quit) Vim을 <strong>종료</strong>한다. (저장되지 않은 변경 사항이 있으면 오류가 발생한다.)
<code>:wq</code> : 파일을 <strong>저장하고 종료</strong>한다. (가장 일반적으로 사용)
<code>:q!</code> : 변경 사항을 저장하지 않고 <strong>강제로 종료</strong>한다.</p>
</li>
</ol>
<p>이제 터미널에 <code>ls</code> 명령어를 입력하면 <code>hello.c</code> 파일이 생성된 것을 확인할 수 있다.</p>
<h2 id="gcc-컴파일러-사용법">GCC 컴파일러 사용법</h2>
<p>GCC(GNU Compiler Collection)는 C, C++, Objective-C 등 다양한 언어를 컴파일할 수 있는 컴파일러 모음이며, 리눅스 환경의 표준 C 컴파일러이다. 컴파일이란 우리가 작성한 C언어 소스 코드(<code>.c</code> 파일)를 컴퓨터가 이해하고 실행할 수 있는 기계어 파일(실행 파일)로 변환하는 과정을 말한다.</p>
<h3 id="gcc로-helloc-컴파일-및-실행하기">GCC로 <code>hello.c</code> 컴파일 및 실행하기</h3>
<p><strong>1) 기본 컴파일</strong>
<code>gcc</code> 명령어 뒤에 컴파일할 소스 파일명을 입력한다.</p>
<pre><code class="language-bash">gcc hello.c</code></pre>
<p>컴파일 과정에서 오류가 없다면 아무런 메시지 없이 명령어 입력이 끝난다. <code>ls</code> 명령어로 확인해 보면 <code>a.out</code>이라는 파일이 새로 생성된 것을 볼 수 있다. 이 <code>a.out</code>이 바로 실행 파일이다.</p>
<p><strong>2) 프로그램 실행</strong>
리눅스 터미널에서 현재 디렉터리에 있는 실행 파일을 실행하려면 파일명 앞에 <code>./</code>를 붙여야 한다.</p>
<pre><code class="language-bash">./a.out</code></pre>
<p><strong>실행 결과:</strong></p>
<pre><code>Hello, World!</code></pre><p><strong>3) 원하는 이름으로 실행 파일 생성 (<code>-o</code> 옵션)</strong>
<code>a.out</code>이라는 기본 이름 대신 원하는 이름으로 실행 파일을 만들고 싶을 때는 <code>-o</code> (output) 옵션을 사용한다.</p>
<pre><code class="language-bash">#사용법: gcc -o [생성할 실행 파일명] [소스 파일명]
gcc -o hello hello.c</code></pre>
<p><code>ls</code> 명령어로 확인하면 <code>a.out</code> 대신 <code>hello</code>라는 이름의 실행 파일이 생성된 것을 볼 수 있다. 실행 방법은 동일하다.</p>
<pre><code class="language-bash">./hello```
**실행 결과:**</code></pre>
<p>Hello, World!</p>
<hr>
<h2 id="vim-모드별-핵심-명령어-정리">Vim 모드별 핵심 명령어 정리</h2>
<p>교수님께서도 처음에는 익숙하지 않을 수 있다고 적어놓고 쓰다보면 외워진다고 하셨다.</p>
<p>근데 평소엔 다른 IDE에서 개발하는 나에게는 실습때마다 도저히 안외워져서 정리해보겠다.</p>
<p>Vim을 처음 사용할 때 다른 것은 잊어버리더라도 아래 4가지만 기억하면 기본적인 문서 작성이 가능하다.</p>
<h3 id="helloc-컴파일에서-사용한-필수-명령어">hello.c 컴파일에서 사용한 필수 명령어</h3>
<table>
<thead>
<tr>
<th align="left">단계</th>
<th align="left">명령어</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>1. 파일 열기</strong></td>
<td align="left"><code>vim 파일명</code></td>
<td align="left">터미널에서 Vim을 실행하며 파일을 연다. (새 파일 생성)</td>
</tr>
<tr>
<td align="left"><strong>2. 입력 시작</strong></td>
<td align="left"><code>i</code></td>
<td align="left"><strong>입력 모드</strong>로 전환하여 텍스트를 입력한다.</td>
</tr>
<tr>
<td align="left"><strong>3. 입력 종료</strong></td>
<td align="left"><code>ESC</code></td>
<td align="left"><strong>명령 모드</strong>로 돌아와 명령을 내릴 준비를 한다.</td>
</tr>
<tr>
<td align="left"><strong>4. 저장 및 종료</strong></td>
<td align="left"><code>:wq</code></td>
<td align="left"><strong>마지막 행 모드</strong>에서 파일을 저장하고 Vim을 종료한다.</td>
</tr>
</tbody></table>
<hr>
<h3 id="명령-모드-normal-mode-명령어">명령 모드 (Normal Mode) 명령어</h3>
<p>Vim의 기본 모드로, 키보드 입력이 명령으로 인식된다. <code>ESC</code> 키를 누르면 언제나 명령 모드로 돌아온다.</p>
<h4 id="커서-이동">커서 이동</h4>
<table>
<thead>
<tr>
<th align="left">명령어</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>h</code>, <code>j</code>, <code>k</code>, <code>l</code></td>
<td align="left">왼쪽, 아래, 위, 오른쪽으로 한 칸씩 이동한다. (방향키 역할)</td>
</tr>
<tr>
<td align="left"><code>w</code></td>
<td align="left">다음 단어(word)의 시작으로 이동한다.</td>
</tr>
<tr>
<td align="left"><code>b</code></td>
<td align="left">이전 단어(word)의 시작으로 이동한다.</td>
</tr>
<tr>
<td align="left"><code>^</code> 또는 <code>0</code></td>
<td align="left">현재 줄의 맨 앞으로 이동한다.</td>
</tr>
<tr>
<td align="left"><code>$</code></td>
<td align="left">현재 줄의 맨 끝으로 이동한다.</td>
</tr>
<tr>
<td align="left"><code>gg</code></td>
<td align="left">파일의 맨 첫 번째 줄로 이동한다.</td>
</tr>
<tr>
<td align="left"><code>G</code></td>
<td align="left">파일의 맨 마지막 줄로 이동한다.</td>
</tr>
<tr>
<td align="left"><code>[숫자]G</code></td>
<td align="left">지정한 숫자의 라인으로 바로 이동한다. (예: <code>15G</code> -&gt; 15번째 줄로 이동)</td>
</tr>
</tbody></table>
<h4 id="편집-수정-및-삭제">편집 (수정 및 삭제)</h4>
<table>
<thead>
<tr>
<th align="left">명령어</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>x</code></td>
<td align="left">현재 커서 위치의 글자 하나를 삭제한다.</td>
</tr>
<tr>
<td align="left"><code>dw</code></td>
<td align="left">커서 위치부터 단어 하나를 삭제한다.</td>
</tr>
<tr>
<td align="left"><code>dd</code></td>
<td align="left">현재 줄 전체를 삭제한다.</td>
</tr>
<tr>
<td align="left"><code>[숫자]dd</code></td>
<td align="left">현재 줄부터 지정한 숫자만큼의 줄을 삭제한다. (예: <code>5dd</code> -&gt; 5줄 삭제)</td>
</tr>
<tr>
<td align="left"><code>r</code></td>
<td align="left">커서 위치의 글자 하나를 다른 글자로 변경한다. (<code>r</code> 누르고 바꿀 글자 입력)</td>
</tr>
<tr>
<td align="left"><code>cw</code></td>
<td align="left">커서 위치부터 단어 하나를 지우고 바로 입력 모드로 전환한다.</td>
</tr>
<tr>
<td align="left"><code>cc</code> 또는 <code>S</code></td>
<td align="left">현재 줄 전체를 지우고 바로 입력 모드로 전환한다.</td>
</tr>
</tbody></table>
<h4 id="복사--붙여넣기-yank--put">복사 &amp; 붙여넣기 (Yank &amp; Put)</h4>
<table>
<thead>
<tr>
<th align="left">명령어</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>yy</code> 또는 <code>Y</code></td>
<td align="left">현재 줄 전체를 복사(Yank)한다.</td>
</tr>
<tr>
<td align="left"><code>[숫자]yy</code></td>
<td align="left">현재 줄부터 지정한 숫자만큼의 줄을 복사한다. (예: <code>3yy</code> -&gt; 3줄 복사)</td>
</tr>
<tr>
<td align="left"><code>p</code></td>
<td align="left">복사한 내용을 현재 커서의 <strong>아래</strong>에 붙여넣기(Put)한다.</td>
</tr>
<tr>
<td align="left"><code>P</code></td>
<td align="left">복사한 내용을 현재 커서의 <strong>위</strong>에 붙여넣기(Put)한다.</td>
</tr>
</tbody></table>
<h4 id="실행-취소-및-반복">실행 취소 및 반복</h4>
<table>
<thead>
<tr>
<th align="left">명령어</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>u</code></td>
<td align="left">이전 명령을 취소한다. (Undo)</td>
</tr>
<tr>
<td align="left"><code>Ctrl</code> + <code>r</code></td>
<td align="left">취소했던 명령을 다시 실행한다. (Redo)</td>
</tr>
<tr>
<td align="left"><code>.</code></td>
<td align="left">바로 직전에 실행했던 명령을 반복한다.</td>
</tr>
</tbody></table>
<hr>
<h3 id="입력-모드-insert-mode-진입-및-탈출-명령어">입력 모드 (Insert Mode) 진입 및 탈출 명령어</h3>
<table>
<thead>
<tr>
<th align="left">명령어</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>i</code></td>
<td align="left">현재 커서 <strong>앞(왼쪽)</strong>에서 입력을 시작한다.</td>
</tr>
<tr>
<td align="left"><code>a</code></td>
<td align="left">현재 커서 <strong>뒤(오른쪽)</strong>에서 입력을 시작한다.</td>
</tr>
<tr>
<td align="left"><code>o</code></td>
<td align="left">현재 줄의 <strong>아래</strong>에 새 줄을 만들고 입력을 시작한다.</td>
</tr>
<tr>
<td align="left"><code>O</code></td>
<td align="left">현재 줄의 <strong>위</strong>에 새 줄을 만들고 입력을 시작한다.</td>
</tr>
<tr>
<td align="left"><code>ESC</code></td>
<td align="left"><strong>명령 모드</strong>로 돌아간다.</td>
</tr>
</tbody></table>
<hr>
<h3 id="마지막-행-모드-command-line-mode-명령어">마지막 행 모드 (Command-Line Mode) 명령어</h3>
<p>명령 모드에서 <code>:</code>(콜론)을 입력하여 진입한다. 화면 하단에 명령어를 입력하여 파일 제어, 검색, 설정 변경 등을 수행한다.</p>
<h4 id="파일-제어"><strong>파일 제어</strong></h4>
<table>
<thead>
<tr>
<th align="left">명령어</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>:w</code></td>
<td align="left">파일을 저장한다. (Write)</td>
</tr>
<tr>
<td align="left"><code>:w 파일명</code></td>
<td align="left">다른 이름으로 파일을 저장한다. (Save As)</td>
</tr>
<tr>
<td align="left"><code>:q</code></td>
<td align="left">Vim을 종료한다. (Quit)</td>
</tr>
<tr>
<td align="left"><code>:wq</code></td>
<td align="left">파일을 저장하고 종료한다.</td>
</tr>
<tr>
<td align="left"><code>:q!</code></td>
<td align="left">변경 내용을 무시하고 강제로 종료한다.</td>
</tr>
</tbody></table>
<h4 id="검색-및-치환"><strong>검색 및 치환</strong></h4>
<table>
<thead>
<tr>
<th align="left">명령어</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>/[검색어]</code></td>
<td align="left">아래 방향으로 검색어를 찾는다. <code>n</code> (다음 찾기), <code>N</code> (이전 찾기)</td>
</tr>
<tr>
<td align="left"><code>?[검색어]</code></td>
<td align="left">위 방향으로 검색어를 찾는다. <code>n</code>, <code>N</code> 키는 동일하게 동작한다.</td>
</tr>
<tr>
<td align="left"><code>:%s/[찾을내용]/[바꿀내용]/g</code></td>
<td align="left">파일 전체(<code>%</code>)에서 &#39;찾을내용&#39;을 &#39;바꿀내용&#39;으로 모두(<code>g</code>) 바꾼다.</td>
</tr>
<tr>
<td align="left"><code>:%s/[찾을내용]/[바꿀내용]/gc</code></td>
<td align="left">바꿀 때마다 사용자에게 확인(confirm)을 받는다.</td>
</tr>
</tbody></table>
<h4 id="에디터-설정"><strong>에디터 설정</strong></h4>
<table>
<thead>
<tr>
<th align="left">명령어</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>:set number</code></td>
<td align="left">줄 번호를 표시한다. (축약형: <code>:set nu</code>)</td>
</tr>
<tr>
<td align="left"><code>:set nonumber</code></td>
<td align="left">줄 번호를 숨긴다. (축약형: <code>:set nonu</code>)</td>
</tr>
<tr>
<td align="left"><code>:set autoindent</code></td>
<td align="left">자동 들여쓰기 기능을 활성화한다.</td>
</tr>
</tbody></table>
<hr>
<p>한번에 다 외울수는 없겠지만 실습때나 <code>Vi 편집기</code>를 사용할때 켜놓고 최대한 기능을 많이 써보며 <code>IDE</code>를 쓸때만큼 편리하게 쓸 수 있도록 여러 명령어들에 익숙해져야겠다.</p>
<p>사용할줄 아는 수준에서 더 나아가 여러 명령어들이 익숙해져 나중에는 <code>VIM 모드</code>가 더 편할 정도의 수준이 되어서 마우스 없이 나의 생각을 그 즉시 키보드로 빠르게 칠 수 있는 수준의 개발자가 되고싶다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 골드와 TOEIC 925점 달성 및 앞으로의 계획]]></title>
            <link>https://velog.io/@kt_gml/%EB%B0%B1%EC%A4%80-%EA%B3%A8%EB%93%9C%EC%99%80-TOEIC-925%EC%A0%90-%EB%8B%AC%EC%84%B1-%EB%B0%8F-%EC%95%9E%EC%9C%BC%EB%A1%9C%EC%9D%98-%EA%B3%84%ED%9A%8D</link>
            <guid>https://velog.io/@kt_gml/%EB%B0%B1%EC%A4%80-%EA%B3%A8%EB%93%9C%EC%99%80-TOEIC-925%EC%A0%90-%EB%8B%AC%EC%84%B1-%EB%B0%8F-%EC%95%9E%EC%9C%BC%EB%A1%9C%EC%9D%98-%EA%B3%84%ED%9A%8D</guid>
            <pubDate>Sat, 11 Oct 2025 07:34:53 GMT</pubDate>
            <description><![CDATA[<h2 id="백준-골드-달성">백준 골드 달성</h2>
<p><strong>전공 공부</strong>를 병행하면서 꾸준히 코딩테스트 문제를 풀다보니 목표 중 하나였던 <strong>백준 골드</strong>를 달성하게 되었다.</p>
<p><strong>실버</strong>를 달성까지 며칠밖에 안 걸렸어서 골드도 금방 달성할 수 있을 줄 알았지만 그건 내 기본기가 있기 때문이었다.</p>
<p>실버 문제부터는 <code>스택, 큐, 트리, 그래프</code>와 같은 자료구조나 <code>BFS, DFS, DP</code>와 같은 고급 알고리즘이 조금씩 사용되어 한 문제 한 문제 성장하면서 풀어야했기에 시간이 훨씬 오래 걸렸다.</p>
<p>티어를 올리기에 집중하기보단 C언어를 통해 문제에 필요한 자료구조들을 직접 구현해보면서 삽질도 해보고 시간을 많이 썼던 것 같다. </p>
<p>또한 문제를 풀때마다 <code>GitHub</code>에 코드들을 커밋했다. </p>
<p>시간이 너무 오래 걸리기도하고 풀이법이 잘 떠오르지도 않아 초반에는 내가 너무 못하는건 아닌가 걱정도 되었다.</p>
<p>하지만 무작위의 실버 수준의 문제들을 풀어낼 수준이 된다면 웬만한 기업의 코딩테스트는 뚫을 수 있다고 해서 걱정이 좀 줄어들었다.</p>
<p>이 정도면 C를 통한 삽질은 충분히 해봤다고 생각한다.</p>
<p>앞으로는 C++의 구현된 자료구조도 활용해보고 Java를 깊게파며 Spring 프레임워크를 활용한 백엔드 공부도 병행하면서 자료구조나 알고리즘의 구현보다는 활용하는 방법을 위주로 코딩테스트를 준비할 것이다.</p>
<p>이 템포라면 졸업할때쯤엔 플레티넘도 달성하고 부트캠프나 각종 기업의 코딩테스트는 가뿐히 넘을 수 있을 수준이 될 것이라고 생각한다. </p>
<hr>
<h2 id="toeic-925점-달성">TOEIC 925점 달성</h2>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/9f6f30c5-90ca-4e7e-a657-45c706484439/image.png" alt=""></p>
<p>RC가 문제라고 생각하고 RC를 보완하고자 ETS 기출문제 3권을 풀어보며 ETS만의 오답을 거르고 정답을 고르는 논리를 이해하고자 노력했다.</p>
<p>또한 어휘와 문법이 부족하다고 생각해 어휘와 문법을 깊게 학습했다.</p>
<p>예를들면 <code>due</code>라는 단어는 to와 함께 <code>~로인한</code> 이라는 뜻으로 알고있었는데 비즈니스 영어인 토익에서는 <code>membership due = 회비</code> 처럼 생각지도 못한 뜻으로 쓰이는 어휘들의 여러 쓰임들과 여러 뜻을 학습하려고 노력했다.</p>
<p>이뿐만아니라 <code>PART 5, 6</code> 위주로 문법을 처음부터 정리하고 엄청 많은 문제를 풀어보았다. </p>
<p>하지만 100문제 타임어택 독해 시험인 토익의 특성상 RC 성적이 그렇게 드라마틱하게 오르진 않았다.</p>
<p>RC 성적을 <strong>400점대</strong>에서 안정적인 <strong>430-440점대</strong>를 만드는데는 성공했지만 더 올리기는 어려웠다.</p>
<p>그래서 LC에 집중하여 LC 만점을 받기위해 노력했고 그 결과 목표와 가까운 <strong>925점</strong>에 달성하는데에 성공했다.</p>
<p><strong>경영학</strong>과 <strong>컴퓨터공학</strong>을 복수전공하며 공부하다보면 <code>영문 원서</code>로 수업하기도하고, 개인 프로젝트를 하다 생기는 오류나 궁금한점들을 찾아보다보면 양질의 자료들이 영어로 되어있는 경우가 많다.</p>
<p>이뿐만 아니라 요즘 미국주식에도 관심이 많은데 어닝콜을 보더라도 영어는 필수적이다. </p>
<p>LLM이나 파파고로 동시 번역도 가능한 요즘 시대에 영어 실력이 개발자가 되는데에 필수적인건 아니지만 영어를 잘하는 개발자가 여러 측면에서 유리하고 개발 분야뿐 아니라 앞으로 살아가면서도 더 빠르게 성장하는데에 꼭 필요한 능력이라고 생각한다. </p>
<p>졸업을 앞두고 오픽을 준비해서 가장 높은 성적인 <code>AL</code>도 받아보고싶다.</p>
<hr>
<h2 id="2학기-목표">2학기 목표</h2>
<ul>
<li><p>오래 기억하고 싶은 내용 VELOG 꾸준히 작성</p>
</li>
<li><p>인스타 키워서 양질의 의류 더 협찬 받기</p>
</li>
<li><p>데이터 관리 강의 활용 - SQLD 취득하기</p>
</li>
<li><p>시흥시 지역문제 경진대회 수상해서 해외연수 경험해보기 </p>
</li>
<li><p>제주시 데이터 활용 공모전 참가해보기</p>
</li>
<li><p>독서 및 투자 공부</p>
</li>
<li><p>운동 및 절주</p>
</li>
<li><p>자료구조 알고리즘 보완 -&gt; PCCP 취득 및 교내 SW 경진대회 수상</p>
</li>
<li><p>웹프로젝트응용 수업 활용 - 운영중인 웹사이트 보완 및 유지보수</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C] 분할 정복 알고리즘과 시프트 연산자
(2630 - 색종이 만들기 / 1074 - Z )]]></title>
            <link>https://velog.io/@kt_gml/C-%EB%B6%84%ED%95%A0-%EC%A0%95%EB%B3%B5-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EA%B3%BC-%EC%8B%9C%ED%94%84%ED%8A%B8-%EC%97%B0%EC%82%B0%EC%9E%902630-%EC%83%89%EC%A2%85%EC%9D%B4-%EB%A7%8C%EB%93%A4%EA%B8%B0-1074-Z</link>
            <guid>https://velog.io/@kt_gml/C-%EB%B6%84%ED%95%A0-%EC%A0%95%EB%B3%B5-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EA%B3%BC-%EC%8B%9C%ED%94%84%ED%8A%B8-%EC%97%B0%EC%82%B0%EC%9E%902630-%EC%83%89%EC%A2%85%EC%9D%B4-%EB%A7%8C%EB%93%A4%EA%B8%B0-1074-Z</guid>
            <pubDate>Tue, 16 Sep 2025 07:55:35 GMT</pubDate>
            <description><![CDATA[<h1 id="분할-정복-알고리즘">분할 정복 알고리즘</h1>
<p><strong>분할 정복 알고리즘(Divide and conquer algorithm)</strong>은 그대로 해결할 수 없는 문제를 작은 문제로 분할하여 문제를 해결하는 방법이다. </p>
<p>대표적인 예로는 <code>퀵 정렬</code>이나 <code>합병 정렬</code>과  <code>이진 탐색</code> 등이 있다.</p>
<hr>
<h2 id="분할-정복-설계">분할 정복 설계</h2>
<p><strong>1) Divide</strong> </p>
<p> 원래 문제가 분할하여 비슷한 유형의 더 작은 하위 문제로 분할이 가능할 때 까지 나눈다.</p>
<p><strong>2) Conquer</strong></p>
<p> 각 하위 문제를 재귀적으로 해결한다. 하위 문제의 규모가 나눌 수 없는 단위가 되면 탈출 조건을 설정하고 해결한다.</p>
<p><strong>3) Combine</strong></p>
<p> <strong>Conquer</strong>한 문제들을 통합하여 원래 문제의 답을 얻어 해결한다.</p>
<hr>
<h2 id="2630---색종이-만들기">2630 - 색종이 만들기</h2>
<p><a href="https://www.acmicpc.net/problem/2630">백준 - 2630 : 색종이 만들기</a></p>
<h3 id="문제">문제</h3>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/2438cde7-6e5b-4971-9102-72c6e8018859/image.png" alt="">
<img src="https://velog.velcdn.com/images/kt_gml/post/d1af3429-6cc7-4066-9591-420ea12b6f04/image.png" alt="">
<img src="https://velog.velcdn.com/images/kt_gml/post/35061317-f994-4a5f-8637-a54bcc6b8847/image.png" alt=""></p>
<h3 id="코드">코드</h3>
<pre><code class="language-c">#include &lt;stdio.h&gt;

int paper[128][128], zero, one;

int search(int x, int y, int n){
    int count = 0;
    for(int i=x; i&lt;x+n; i++){
        for(int j=y; j&lt;y+n; j++){
            if(paper[i][j]==1) count++; 
        }
    }
    if(count == n * n) one++;
    else if(count == 0) zero++;
    else{
        search(x, y, n/2);
        search(x, y+n/2, n/2);
        search(x+n/2, y, n/2);
        search(x+n/2, y+n/2, n/2);
    }
}

int main(void){
    int n;
    scanf(&quot;%d&quot;, &amp;n);
    for(int i=0; i&lt;n; i++){
        for(int j=0; j&lt;n; j++){
            scanf(&quot;%d&quot;, &amp;paper[i][j]);
        }
    }
    search(0, 0, n);
    printf(&quot;%d\n%d&quot;, zero, one);
}</code></pre>
<p><strong>2차원 배열</strong>을 활용했고, 1개의 정사각형을 1, 2, 3, 4사분면으로 나누어 조건이 만족될때마다 count를 높여가며 재귀적으로 문제를 풀었다.</p>
<p>이때 count와 n을 비교한 값을 종료 조건으로 설정하고 n/2으로 절반씩 줄여나가는 방법을 사용했다. </p>
<p>같은 원리로 Z라는 문제를 접근했는데 시간 초과 문제가 생겼다.</p>
<hr>
<h2 id="1074---z">1074 - Z</h2>
<p><a href="https://www.acmicpc.net/problem/1074">백준 - 1074 : Z</a></p>
<h3 id="문제-1">문제</h3>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/a14732ad-52d1-4343-b31a-727af786df6b/image.png" alt="">
<img src="https://velog.velcdn.com/images/kt_gml/post/4c375d32-ece6-487a-9643-752c817955c4/image.png" alt=""></p>
<h3 id="코드-1">코드</h3>
<pre><code class="language-c">#include &lt;stdio.h&gt;

int count = 0; // 전역변수 초기화
int r, c;

int search(int x, int y, int n) { 
    if (n == 1) {
        if (x == r &amp;&amp; y == c) {
            return 1; // 찾았을 때 1 반환
        }
    } else {
        int half = n / 2;

        // 1사분면
        if (r &lt; x + half &amp;&amp; c &lt; y + half) {
            return search(x, y, half);
        } else {
            count += half * half;
        }

        // 2사분면
        if (r &lt; x + half &amp;&amp; c &gt;= y + half) {
            return search(x, y + half, half);
        } else {
            count += half * half;
        }

        // 3사분면
        if (r &gt;= x + half &amp;&amp; c &lt; y + half) {
            return search(x + half, y, half);
        } else {
            count += half * half;
        }

        // 4사분면
        if (r &gt;= x + half &amp;&amp; c &gt;= y + half) {
            return search(x + half, y + half, half);
        } else {
            count += half * half;
        }
        return 0;
    }
}

int main() {
    int n;
    scanf(&quot;%d %d %d&quot;, &amp;n, &amp;r, &amp;c);

    int size = 1 &lt;&lt; n; // 2^n 계산하기 위한 비트연산

    search(0, 0, size); // 좌표 0, 0 부터 탐색

    printf(&quot;%d\n&quot;, count);
    return 0;
}</code></pre>
<p>처음엔 <code>색종이 만들기</code> 문제와 같이 모든 사분면을 조사해보며 함수가 호출 될때마다 <code>count</code>를 증가시키는 방식을 사용했다.</p>
<p>예제까지는 잘 실행됐지만 2의 지수꼴로 조사해야하는 범위가 늘어날수록 시간이 기하 급수적으로 늘어나다보니 시간 초과 문제가 생겼다.</p>
<p>그래서 이를 해결하고자 각 사분면을 하나하나 조사하는게 아닌 <code>count+=n*n</code>을 통해 시간을 엄청나게 줄일 수 있었다.</p>
<p>이렇게 분할 정복에다가 조사하지 않아도 되는 부분을 <code>n*n</code>과 같은 방법으로 넘기는 방법까지 더해지니 2의 지수꼴과 같이 규모가 커짐에 따라 얼마나 크게 실행 속도의 차이를 만들어 내는지 느낄 수 있었다. </p>
<p>이런 알고리즘과 자료구조를 기반으로 효율적으로 구현한 코드 하나하나가 적은 비용으로 효과적으로 운영할 수 있는 서비스의 기반이 되는 것이다.</p>
<hr>
<h2 id="시프트-연산자--를-활용한-지수-계산">시프트 연산자(&lt;&lt; / &gt;&gt;)를 활용한 지수 계산</h2>
<p><strong>시프트 연산</strong>은 좌항에 있는 피연산자를 우항에 있는 수만큼 비트 자리 이동하는 연산을 수행한다.</p>
<p><code>&lt;&lt;</code> 는 왼쪽 쉬프트 연산자이고 <code>&gt;&gt;</code> 는 오른쪽 쉬프트 연산자이다.</p>
<p>왼쪽 쉬프트 연산을 하면 좌항에 있는 피연산자의 값이 우항에 있는 수만큼 왼쪽으로 자리 이동하고 빈 자리는 0으로 채우게 된다.</p>
<p>이 특성을 이용한다면 2의 거듭제곱을 Z 문제의 코드처럼 쉽게 구할 수 있다.</p>
<pre><code class="language-c">    printf(&quot;%u\n&quot;, num1 &lt;&lt; 1);    //   2: 0000 0010: 2
    printf(&quot;%u\n&quot;, num1 &lt;&lt; 2);    //   4: 0000 0100: 2^2
    printf(&quot;%u\n&quot;, num1 &lt;&lt; 3);    //   8: 0000 1000: 2^3
    printf(&quot;%u\n&quot;, num1 &lt;&lt; 4);    //  16: 0001 0000: 2^4
    printf(&quot;%u\n&quot;, num1 &lt;&lt; 5);    //  32: 0010 0000: 2^5
    printf(&quot;%u\n&quot;, num1 &lt;&lt; 6);    //  64: 0100 0000: 2^6
    printf(&quot;%u\n&quot;, num1 &lt;&lt; 7);    // 128: 1000 0000: 2^7</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C] 컴파일러의 차이로 발생하는 오류(Undefined Behavior)]]></title>
            <link>https://velog.io/@kt_gml/C-%EC%BB%B4%ED%8C%8C%EC%9D%BC%EB%9F%AC%EC%9D%98-%EC%B0%A8%EC%9D%B4%EB%A1%9C-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EC%98%A4%EB%A5%98Undefined-Behavior</link>
            <guid>https://velog.io/@kt_gml/C-%EC%BB%B4%ED%8C%8C%EC%9D%BC%EB%9F%AC%EC%9D%98-%EC%B0%A8%EC%9D%B4%EB%A1%9C-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EC%98%A4%EB%A5%98Undefined-Behavior</guid>
            <pubDate>Thu, 17 Jul 2025 06:21:56 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/25501">백준 25501 - 재귀의 귀재</a></p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/0920694b-8bbe-4238-9af2-f9493b3cda4d/image.png" alt=""></p>
<p><strong>재귀 알고리즘</strong> 문제를 풀다가 답안을 제출했는데 자꾸 오답처리가 되었다.</p>
<p>알고리즘에도 문제가 없고 내 컴파일러에서는 실행결과에도 아무런 문제가 없었다.</p>
<p>그래서 다른 사람들의 코드와 비교하다가 이유를 찾았다.</p>
<pre><code class="language-c">오답 코드

#define _CRT_SECURE_NO_WARNINGS
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;

int recursion(const char *s, int l, int r, int *count) {
  (*count)++;
  if (l &gt;= r)
    return 1;
  else if (s[l] != s[r])
    return 0;
  else
    return recursion(s, l + 1, r - 1, count);
}

int isPalindrome(const char *s, int *count) {
  return recursion(s, 0, strlen(s) - 1, count);
}

int main() {
  int num, count, temp;
  char s[1002];
  scanf(&quot;%d&quot;, &amp;num);
  while (num--) {
    count = 0;
    scanf(&quot;%s&quot;, s);
    printf(&quot;%d %d\n&quot;, isPalindrome(s, &amp;count), count);
  }
  return 0;
}</code></pre>
<hr>
<pre><code class="language-c">정답 코드
#define _CRT_SECURE_NO_WARNINGS
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;

int recursion(const char *s, int l, int r, int *count) {
  (*count)++;
  if (l &gt;= r)
    return 1;
  else if (s[l] != s[r])
    return 0;
  else
    return recursion(s, l + 1, r - 1, count);
}

int isPalindrome(const char *s, int *count) {
  return recursion(s, 0, strlen(s) - 1, count);
}

int main() {
  int num, count, temp;
  char s[1002];
  scanf(&quot;%d&quot;, &amp;num);
  while (num--) {
    count = 0;
    scanf(&quot;%s&quot;, s);
    temp = isPalindrome(s, &amp;count);
    printf(&quot;%d %d\n&quot;, temp, count);
  }
  return 0;
}</code></pre>
<hr>
<pre><code class="language-c">오류 부분
    printf(&quot;%d %d\n&quot;, isPalindrome(s, &amp;count), count);


수정 후    
    temp = isPalindrome(s, &amp;count);
    printf(&quot;%d %d\n&quot;, temp, count);</code></pre>
<p>결론부터 말하자면, <strong>채점 서버의 컴파일러가 함수 인자를 처리하는 순서가 사용자의 로컬 환경과 달랐기 때문</strong>에 발생한 오류였다.</p>
<hr>
<h2 id="함수의-인자-평가-순서">함수의 인자 평가 순서</h2>
<p>C 언어 표준에서는 함수에 여러 개의 인자(argument)를 전달할 때, <strong>컴파일러가 그 인자들의 값을 어떤 순서로 계산(평가)할지에 대해 정해두지 않았다.</strong></p>
<p>즉, 아래와 같은 코드가 있을 때,
*<em><code>printf(&quot;%d %d\n&quot;, isPalindrome(s, &amp;count), count);</code>
*</em>
컴파일러는 두 가지 방식 중 아무거나 선택할 수 있다.</p>
<p><strong>1. 인자를 오른쪽에서 왼쪽으로 평가 (많은 C 컴파일러의 일반적인 방식)</strong></p>
<ol>
<li><code>printf</code>의 두 번째 인자인 <code>count</code>를 먼저 평가한다. </li>
<li>그 다음, 첫 번째 인자인 <code>isPalindrome(s, &amp;count)</code>를 평가한다.</li>
<li><code>isPalindrome</code> 함수가 실행되면서 내부의 <code>recursion</code>이 호출되고, <code>count</code>의 값이 (예를 들어) <strong>3</strong>으로 변경된다. 함수는 팰린드롬 여부에 따라 1 또는 0을 반환한다.</li>
<li><code>printf</code> 함수는 최종적으로 (2)번 단계에서 반환된 값과 (1)번 단계에서 <strong>미리 계산해 둔 <code>count</code>의 값(0)</strong>을 가지고 출력한다.</li>
<li><strong>결과: <code>1 0</code> (오답)</strong></li>
</ol>
<p><strong>2. 인자를 왼쪽에서 오른쪽으로 평가</strong></p>
<ol>
<li><code>printf</code>의 첫 번째 인자인 <code>isPalindrome(s, &amp;count)</code>를 먼저 평가한다.</li>
<li>함수가 실행되면서 <code>count</code>의 값이 <strong>3</strong>으로 변경되고, 함수는 1 또는 0을 반환한다.</li>
<li>그 다음, 두 번째 인자인 <code>count</code>를 평가한다. 이 시점에서 <code>count</code>의 값은 (2)번 단계에서 변경된 <strong>3</strong>이다다.</li>
<li><code>printf</code> 함수는 (1)번 단계의 반환값과 (3)번 단계에서 계산된 <code>count</code>의 값(3)을 가지고 출력한다.</li>
<li><strong>결과: <code>1 3</code> (정답)</strong></li>
</ol>
<p>백준의 채점 서버의 컴파일러는 <strong>방식 1</strong>처럼 동작했고, 나의 컴파일러는 <strong>방식 2</strong>처럼 동작해서 생긴 오류였다. </p>
<p>이처럼 컴파일러나 환경에 따라 결과가 달라질 수 있는 코드는 <strong>정의되지 않은 동작(Undefined Behavior)</strong>에 의존하는 코드라고 부르며, 프로그래밍 시 반드시 피해야 한다.</p>
<hr>
<h2 id="temp-변수를-사용한-순서-보장">temp 변수를 사용한 순서 보장</h2>
<p><code>temp</code> 변수를 사용한 코드는 왜 항상 올바르게 동작할까 ?</p>
<pre><code class="language-c">temp = isPalindrome(s, &amp;count);       // 1번 라인
printf(&quot;%d %d\n&quot;, temp, count);      // 2번 라인</code></pre>
<p>C 언어에서 세미콜론(<code>;</code>)은 <strong>&quot;시퀀스 포인트(Sequence Point)&quot;</strong> 역할을 한다. </p>
<p>시퀀스 포인트는 이전 라인에서 발생한 모든 계산과 <strong>부수 효과(side effect)</strong>가 다음 라인으로 넘어가기 전에 반드시 완료됨을 보장한다.</p>
<ol>
<li><strong>1번 라인 실행</strong>: <code>isPalindrome(s, &amp;count)</code>가 호출됩니다. 이 함수의 실행이 <strong>완전히 끝날 때까지</strong> 프로그램은 다음으로 넘어가지 않는다.<ul>
<li>이 과정에서 <code>count</code>의 값은 최종값(예: 3)으로 업데이트된다.</li>
<li><code>isPalindrome</code>의 반환값은 <code>temp</code> 변수에 저장된다.</li>
</ul>
</li>
<li><strong>시퀀스 포인트(<code>;</code>)</strong>: 1번 라인의 모든 동작(count 값 변경 포함)이 완료되었음을 보장한다.</li>
<li><strong>2번 라인 실행</strong>: <code>printf</code>가 호출된다.<ul>
<li>이 시점에는 <code>temp</code>와 <code>count</code>의 값이 이미 <strong>모두 확정된 상태</strong>이다.</li>
<li><code>printf</code>의 인자 평가 순서가 어떻게 되든 상관없이, 이미 계산이 끝난 <code>temp</code>의 값과 <code>count</code>의 최종값을 가져와 출력하므로 항상 올바른 결과가 나온다.</li>
</ul>
</li>
</ol>
<hr>
<p>앞으론 세미콜론(시퀀스 포인트)을 통해 <strong>연산의 순서를 명확하게 강제</strong>하는 등 C의 동작과정을 더 이해하고 어떤 컴파일러 환경에서도 동일한 결과를 보장하는 코드를 작성해야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C] 스택과 후위 표기식(1918 - 후위 표기식)]]></title>
            <link>https://velog.io/@kt_gml/C-%EC%8A%A4%ED%83%9D%EA%B3%BC-%ED%9B%84%EC%9C%84-%ED%91%9C%EA%B8%B0%EC%8B%9D1918-%ED%9B%84%EC%9C%84-%ED%91%9C%EA%B8%B0%EC%8B%9D</link>
            <guid>https://velog.io/@kt_gml/C-%EC%8A%A4%ED%83%9D%EA%B3%BC-%ED%9B%84%EC%9C%84-%ED%91%9C%EA%B8%B0%EC%8B%9D1918-%ED%9B%84%EC%9C%84-%ED%91%9C%EA%B8%B0%EC%8B%9D</guid>
            <pubDate>Mon, 14 Jul 2025 11:18:31 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-링크">문제 링크</h2>
<p><a href="https://www.acmicpc.net/problem/1918">백준 - 후위 표기식</a></p>
<hr>
<h2 id="문제-내용">문제 내용</h2>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/48219334-d10f-450f-b2b1-7441fdf632f6/image.png" alt=""></p>
<p><strong>C</strong>에는 스택 라이브러리가 없기 때문에 구현하여 사용해야한다.</p>
<p>그래서 스택을 구현한 김에 더 깊게 이해하고자 스택 문제를 여러 개 풀고 있다.</p>
<hr>
<h2 id="후위-표기식postfix-notation이란">후위 표기식(Postfix Notation)이란?</h2>
<p>우리는 1+2+3 처럼 <code>숫자(피연산자)</code>와 <code>숫자</code> 사이에 <code>연산자(+)</code>를 넣어서 식을 표현하는데 </p>
<p>이를 <strong>중위 표기식(Infix Notation)</strong>이라고 한다.</p>
<p><strong>후위 표기식(Postfix Notation)</strong>은 <code>연산자</code>가 <code>피연산자</code>의 뒤에 온다.</p>
<pre><code>1 + 2 + 3 (Infix)

1 2 + 3 + (Postfix)</code></pre><h3 id="컴퓨터가-후위-표기식을-사용하는-이유">컴퓨터가 후위 표기식을 사용하는 이유</h3>
<p><strong>후위 표기식</strong>은 앞에서부터 뒤로 가면서 연산자가 보이면 앞의 두 숫자를 피연산자로 계산하면 된다.</p>
<p><strong>중위 표기식</strong> <code>1+2×3</code>의 경우 <strong>후위 표기식</strong>으론 <code>123×+</code>이다.</p>
<p>앞에서부터 차례로 읽다가 연산자인 ×가 발견되었을 때 앞에 있는 2개의 피연산자 2 3를 대상으로 2×3=6을 실행한다.</p>
<p>그 뒤의 연산자인 +가 발견되었을 때 앞에 있는 2개의 피연산자 1 6을 대상으로 1+6=7을 실행한다.
<br></p>
<p>컴퓨터는 후위 표기식을 사용하는데, 이런 <strong>후위 표기식</strong>을 컴퓨터가 사용하는 근본적인 이유는 </p>
<p><strong>괄호</strong>도 필요없고 <strong>연산자 우선 순위를 고려</strong>할 필요도 없기 때문이다.</p>
<h3 id="후위-표기식과-스택">후위 표기식과 스택</h3>
<p>이러한 방식의 <strong>후위 표기식</strong>을 만들거나 <strong>후위 표기식</strong>을 <strong>중위 표기식</strong>으로 만들때 스택(Stack) 구조가 사용된다.</p>
<p>간단한 예를 들면 아까처럼 <code>123x+</code>을 중위 표기식으로 사용할때 <code>1/2/3</code>을 스택에 담고 <code>x</code>를 만나면</p>
<p><code>pop() x pop()</code> 후 연산한 값을 <code>push()</code>한다면 스택에는 <code>1/6</code>이 들어가 있을 것이다.</p>
<p>이 예시는 후위 표기식을 중위 표기식으로 만드는 과정이었고, 이 문제는 반대로 중위 표기식을 후위 표기식으로 만드는 문제이다.</p>
<hr>
<h2 id="중위-표기식---후위-표기식">중위 표기식 -&gt; 후위 표기식</h2>
<p>먼저 코드를 작성하기 전 4가지 규칙을 찾았다.</p>
<ol>
<li><p>피연산자는 출력한다.</p>
</li>
<li><p>&#39;(&#39;를 만난다면 push한다.</p>
</li>
<li><p>&#39;)&#39;를 만난다면 &#39;(&#39;가 나올때까지 pop을 진행하고 그 사이의 연산자들을 출력한다.</p>
</li>
<li><p>연산자를 만난다면 push하지만 이때 peek의 우선순위가 연산자의 우선순위보다 크거나 같다면 우선순위가 더 작아질때까지 pop 후 출력하고 연산자를 push한다.</p>
</li>
</ol>
<p>이 조건을 잘 신경쓴다면 문제없이 코드를 구현할 수 있다.</p>
<hr>
<h2 id="코드">코드</h2>
<pre><code class="language-c">#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#define MAX_STACK_SIZE 100

typedef char element;

typedef struct{
  element data[MAX_STACK_SIZE];
  int top; 
}StackType;

void init_stack(StackType *s){
  s-&gt;top = -1;
}

int is_empty(StackType *s){
  return(s-&gt;top == -1);
}

int is_full(StackType *s){
  return(s-&gt;top == MAX_STACK_SIZE-1);
}

void push(StackType *s, element item){
  if(is_full(s)){
    return;
  }
  s-&gt;data[++(s-&gt;top)] = item;
}

element pop(StackType *s){
  if(is_empty(s)){
    return 0;
  }
  return s-&gt;data[(s-&gt;top)--];
}

int priority(char c){
  if(c == &#39;(&#39; || c==&#39;)&#39;)
    return 0; //&#39;()&#39;은 연산에 참가 안하고 따로 처리하므로 후순위로 둠
  else if(c == &#39;+&#39; || c == &#39;-&#39;) 
    return 1;
  else if(c == &#39;*&#39; || c == &#39;/&#39;)
    return 2;
}

int main(void){
  char input[100] = {0,};
  scanf(&quot;%s&quot;, input);
  StackType s;
  char temp;
  init_stack(&amp;s);
  for(int i=0; i&lt;strlen(input); i++){
    int ch = input[i];
    switch(ch){
      case &#39;+&#39;: case &#39;-&#39; : case &#39;*&#39; : case &#39;/&#39;:
        while(!is_empty(&amp;s) &amp;&amp; priority(s.data[s.top])&gt;=priority(ch)){ 
        //스택에 있는 연산자의 우선순위가 더 크거나 같으면 출력
          printf(&quot;%c&quot;, pop(&amp;s));
        }
        push(&amp;s, ch);
        break;

      case &#39;(&#39;: 
        push(&amp;s, ch);
        break;

      case &#39;)&#39;:
        temp = pop(&amp;s);
        while(temp !=&#39;(&#39;){
          printf(&quot;%c&quot;, temp);
          temp = pop(&amp;s);
        }
        break;

      default : //피연산자인 경우
        printf(&quot;%c&quot;, ch);
        break;
    }
  }
  while(!is_empty(&amp;s)){ //남은 연산자 출력
    printf(&quot;%c&quot;, pop(&amp;s));
  }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C] 1874 - 스택 수열]]></title>
            <link>https://velog.io/@kt_gml/C-1874-%EC%8A%A4%ED%83%9D-%EC%88%98%EC%97%B4</link>
            <guid>https://velog.io/@kt_gml/C-1874-%EC%8A%A4%ED%83%9D-%EC%88%98%EC%97%B4</guid>
            <pubDate>Sun, 13 Jul 2025 16:27:06 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-링크">문제 링크</h2>
<p><a href="https://www.acmicpc.net/problem/1874">백준 - 스택 수열</a></p>
<hr>
<h2 id="문제-내용">문제 내용</h2>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/3512fca4-2659-47ed-8ae4-369f8ff36962/image.png" alt=""></p>
<p><strong>C</strong>에는 스택 라이브러리가 없기 때문에 구현하여 사용해야한다.</p>
<p>그래서 이왕 스택을 구현한 김에 더 깊게 이해하고자 스택 문제를 여러개 풀고 있다.</p>
<p>아마 예시 입출력이 없었다면 아직도 문제를 이해하지 못했을 것이다.</p>
<p>쉽게 설명하자면 <strong>1부터 오름차순</strong>으로 스택에 <strong>push</strong> 할 수 있고 <strong>pop</strong>을 통해 입력된 수열을 만들 수 있는지 체크하고 만들 수 있다면 push와 pop의 과정들을 출력하는 문제이다.</p>
<p><strong>세가지 조건</strong>을 나누어서 해결했다. </p>
<p>우선 peek라고도 불리는 <strong>stack[top]</strong> 값과 <strong>temp(입력값)</strong>을 비교했다.</p>
<p><strong>1. stack[top]값이 더 큰 경우</strong>
&quot;NO&quot; 출력 후 프로그램 종료</p>
<p><strong>2. temp값이 더 큰 경우</strong>
 stack[top]과 temp가 같아질때까지 push 후 같아지면 pop 진행</p>
<p><strong>3. 두 값이 같은 경우</strong>
pop 진행</p>
<p>이 세 가지 경우로 나누어 코드를 짰다.</p>
<hr>
<h2 id="코드">코드</h2>
<pre><code class="language-c">#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#define MAX_STACK_SIZE 100

typedef char element;

typedef struct{
  element data[MAX_STACK_SIZE];
  int top; 
}StackType;

void init_stack(StackType *s){
  s-&gt;top = -1;
}

int is_empty(StackType *s){
  return(s-&gt;top == -1);
}

int is_full(StackType *s){
  return(s-&gt;top == MAX_STACK_SIZE-1);
}

void push(StackType *s, element item){
  if(is_full(s)){
    return;
  }
  s-&gt;data[++(s-&gt;top)] = item;
}

element pop(StackType *s){
  if(is_empty(s)){
    return 0;
  }
  return s-&gt;data[(s-&gt;top)--];
}

int priority(char c){
  if(c == &#39;(&#39; || c==&#39;)&#39;)
    return 0; //&#39;()&#39;은 연산에 참가 안하고 따로 처리하므로 후순위로 둠
  else if(c == &#39;+&#39; || c == &#39;-&#39;) 
    return 1;
  else if(c == &#39;*&#39; || c == &#39;/&#39;)
    return 2;
}

int main(void){
  char input[100] = {0,};
  scanf(&quot;%s&quot;, input);
  StackType s;
  char temp;
  init_stack(&amp;s);
  for(int i=0; i&lt;strlen(input); i++){
    int ch = input[i];
    switch(ch){
      case &#39;+&#39;: case &#39;-&#39; : case &#39;*&#39; : case &#39;/&#39;:
        while(!is_empty(&amp;s) &amp;&amp; priority(s.data[s.top])&gt;=priority(ch)){
          printf(&quot;%c&quot;, pop(&amp;s));
        }
        push(&amp;s, ch);
        break;

      case &#39;(&#39;:
        push(&amp;s, ch);
        break;

      case &#39;)&#39;:
        temp = pop(&amp;s);
        while(temp !=&#39;(&#39;){
          printf(&quot;%c&quot;, temp);
          temp = pop(&amp;s);
        }
        break;

      default : //피연산자인 경우
        printf(&quot;%c&quot;, ch);
        break;
    }
  }
  while(!is_empty(&amp;s)){
    printf(&quot;%c&quot;, pop(&amp;s));
  }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JSP] DAO 패턴과 커넥션 풀을 활용한  회원가입 및 로그인 기능 구현]]></title>
            <link>https://velog.io/@kt_gml/JSP-DAO-%ED%8C%A8%ED%84%B4-%EC%BB%A4%EB%84%A5%EC%85%98-%ED%92%80-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85-%EB%B0%8F-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@kt_gml/JSP-DAO-%ED%8C%A8%ED%84%B4-%EC%BB%A4%EB%84%A5%EC%85%98-%ED%92%80-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85-%EB%B0%8F-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Thu, 10 Jul 2025 17:24:01 GMT</pubDate>
            <description><![CDATA[<h1 id="dao-패턴과-커넥션-풀">DAO 패턴과 커넥션 풀</h1>
<p>비즈니스 로직을 JSP에서 분리했더라도, 데이터베이스 연결(JDBC) 코드가 서블릿이나 JSP에 직접 포함되어 있다면 심각한 문제를 야기한다.</p>
<pre><code class="language-java">// 서블릿이나 JSP 파일 내부 - 좋지 않은 예시
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;

try {
    // 1. 드라이버 로딩
    Class.forName(&quot;oracle.jdbc.driver.OracleDriver&quot;);
    // 2. 커넥션 생성
    conn = DriverManager.getConnection(&quot;jdbc:oracle:thin:@localhost:1521:XE&quot;, &quot;user&quot;, &quot;pass&quot;);
    // 3. SQL 작성 및 실행
    String sql = &quot;SELECT * FROM member WHERE id = ?&quot;;
    pstmt = conn.prepareStatement(sql);
    pstmt.setString(1, request.getParameter(&quot;id&quot;));
    rs = pstmt.executeQuery();
    // 4. 결과 처리...
    // 이 모든 코드가 DB 작업이 필요한 모든 파일에 중복되어 삽입된다.
} catch(Exception e) {
    // 예외 처리
} finally {
    // 5. 자원 해제 (매번 반복)
    if(rs != null) rs.close();
    if(pstmt != null) pstmt.close();
    if(conn != null) conn.close();
}</code></pre>
<p>이러한 구조는 코드의 <strong>중복</strong>이 극심하고, DB 정보(URL, ID, PW)가 변경되면 관련된 모든 파일을 수정해야 하는 <strong>유지보수 재앙</strong>을 초래한다. </p>
<p>또한, 매 요청마다 DB 커넥션을 생성하고 해제하는 과정은 시스템에 엄청난 부하를 주어 <strong>성능 저하의 주범</strong>이 된다.</p>
<p>해결책으로 <strong>DAO(Data Access Object) 패턴</strong>을 도입하여 데이터 로직을 분리하고, <strong>커넥션 풀(Connection Pool)</strong>을 함께 사용하여 시스템의 효율을 극대화해야 한다.</p>
<hr>
<h2 id="dao-패턴">DAO 패턴</h2>
<p><strong>DAO</strong>는 데이터베이스에 접근하고 처리하는 로직(CRUD: Create, Read, Update, Delete)을 <strong>별도의 클래스로 완전히 분리</strong>하여 캡슐화하는 디자인 패턴이다. </p>
<p>이는 &#39;관심사의 분리(Separation of Concerns)&#39; 원칙을 충실히 따르는 방법이다.</p>
<p>서블릿이나 다른 서비스 클래스들은 복잡한 JDBC 코드나 SQL 문을 전혀 알 필요가 없다. </p>
<p>그저 DAO 객체에게 &quot;회원 정보 저장&quot;, &quot;ID가 &#39;user1&#39;인 회원 정보 조회&quot; 와 같이 <strong>의미 있는 기능 단위로 요청</strong>만 하면 된다.</p>
<hr>
<h2 id="dto-vo-그리고-자바빈javabean">DTO, VO 그리고 자바빈(JavaBean)</h2>
<p><strong>DTO (Data Transfer Object) / VO (Value Object)</strong> 
계층 간(Controller-Service-DAO) 데이터를 전달하기 위한 목적으로 만들어진 객체이다. 
예를 들면 <code>MemberDTO</code>는 회원 한 명의 정보를 온전히 담아 운반하는 &#39;데이터 상자&#39; 역할을 한다.</p>
<p><strong>자바빈 (JavaBean)</strong> 
앞서 다뤘던 자바빈은 특정 규칙을 따르는 자바 클래스를 지칭하는 <strong>기술 규약</strong>이다.
    1.  <code>private</code> 접근 제한자로 필드를 선언한다.
    2.  <code>public</code> 접근자로 <code>getter/setter</code> 메서드를 가진다.
    3.  인자 없는 기본 생성자(<code>default constructor</code>)를 가진다.</p>
<p><strong>하지만 여기서 DTO는 자바빈 규약에 따라 만드는 것이 일반적이다.</strong> </p>
<p>즉, <code>MemberDTO</code>는 &#39;데이터 전달&#39;이라는 <strong>역할(패턴)</strong>을 수행하며, 그 구현은 <strong>자바빈(기술 규약)</strong>을 따른다. </p>
<p>따라서 &quot;DTO는 자바빈이다&quot;라고 말할 수 있다. </p>
<p>JSP에서 <code>&lt;jsp:useBean&gt;</code> 액션 태그가 DTO 객체를 쉽게 다룰 수 있는 것도 DTO가 자바빈 규약을 따르기 때문이다.</p>
<hr>
<h2 id="dao-data-access-object">DAO (Data Access Object)</h2>
<p>실제 DB 작업을 수행하는 객체이다. </p>
<p>모든 JDBC 코드는 DAO 클래스 내부에만 존재한다.</p>
<pre><code class="language-java">// MemberDAO.java 의 예시
public class MemberDAO {
    // ... Connection Pool을 통해 Connection을 얻어오는 로직 ...

    public int insertMember(MemberDTO dto) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        String sql = &quot;INSERT INTO member (id, password, name, email) VALUES (?, ?, ?, ?)&quot;;
        int result = 0;
        try {
            conn = getConnection(); // 커넥션 풀에서 하나 빌려오기
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, dto.getId());
            pstmt.setString(2, dto.getPassword());
            pstmt.setString(3, dto.getName());
            pstmt.setString(4, dto.getEmail());
            result = pstmt.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(pstmt, conn); // 자원 반납
        }
        return result;
    }
    // getMember(id), updateMember(dto), deleteMember(id) 등 다른 메서드들...
}</code></pre>
<hr>
<h2 id="커넥션-풀connection-pool">커넥션 풀(Connection Pool)</h2>
<p>데이터베이스 커넥션을 맺고 끊는 작업은 네트워크 통신, 사용자 인증, 세션 설정 등이 포함된, 시스템에 상당한 부하를 주는 <strong>고비용 작업</strong>이다. </p>
<p>매 요청마다 이 작업을 반복하는 것은 극심한 성능 저하를 유발한다.</p>
<p><strong>커넥션 풀</strong>은 이 문제를 해결하기 위해 미리 일정 개수의 <strong>DB 커넥션을 생성하여 &#39;풀(Pool)&#39;이라는 저장 공간에 보관</strong>해두고, 필요할 때마다 빌려 쓰고 반납하는 기법이다.</p>
<p><strong>동작 원리:</strong></p>
<ol>
<li><p><strong>WAS 시작</strong> 
웹 애플리케이션 서버(EX:Tomcat)가 시작될 때, <code>context.xml</code>에 설정된 정보를 읽어 지정된 개수만큼 커넥션을 미리 생성하여 풀에 저장한다.</p>
</li>
<li><p><strong>DAO의 요청</strong> 
<code>MemberDAO</code>가 DB 작업이 필요하면, <code>DriverManager.getConnection()</code>을 직접 호출하는 대신, WAS가 제공하는 <strong>JNDI(Java Naming and Directory Interface)</strong> 서비스를 통해 커넥션 풀을 찾는다.</p>
</li>
<li><p><strong>대여(Borrow)</strong> 
JNDI를 통해 찾은 커넥션 풀(DataSource 객체)에게 <code>dataSource.getConnection()</code>을 호출하여 커넥션을 요청한다. 풀은 유휴 상태의 커넥션 하나를 빌려준다.</p>
</li>
<li><p><strong>작업 수행</strong> 
DAO는 빌린 커넥션을 사용하여 SQL 작업을 수행한다.</p>
</li>
<li><p><strong>반납(Return)</strong> 
작업이 끝나면 <code>connection.close()</code>를 호출한다. 
커넥션 풀을 통해 얻은 커넥션은 이 때 실제로 닫히는 것이 아니라, <strong>풀에 반납되어 &#39;대기&#39; 상태</strong>로 돌아간다.</p>
</li>
<li><p><strong>재사용</strong> 
다른 요청이 들어오면, 풀에 대기 중인 커넥션을 다시 빌려주어 커넥션 생성 비용 없이 즉시 DB 작업을 시작할 수 있다.</p>
</li>
</ol>
<p><strong>Tomcat <code>context.xml</code> 설정 예시</strong>
<code>META-INF/context.xml</code> 파일에 다음과 같이 DB 정보를 설정한다.</p>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;Context&gt;
    &lt;Resource name=&quot;jdbc/OracleDB&quot; auth=&quot;Container&quot;
              type=&quot;javax.sql.DataSource&quot;
              driverClassName=&quot;oracle.jdbc.driver.OracleDriver&quot;
              url=&quot;jdbc:oracle:thin:@localhost:1521:XE&quot;
              username=&quot;myuser&quot;
              password=&quot;mypassword&quot;
              maxTotal=&quot;20&quot;   &lt;!-- 최대 커넥션 개수 --&gt;
              maxIdle=&quot;10&quot;    &lt;!-- 유휴 상태로 유지할 최대 커넥션 개수 --&gt;
              maxWaitMillis=&quot;5000&quot; /&gt; &lt;!-- 커넥션을 기다리는 최대 시간(ms) --&gt;
&lt;/Context&gt;</code></pre>
<p><strong>커넥션 풀 사용 코드 (DAO 내부)</strong></p>
<pre><code class="language-java">// DAO에서 커넥션을 얻어오는 메서드
private Connection getConnection() {
    Connection conn = null;
    try {
        Context initContext = new InitialContext();
        // context.xml 에 설정한 &quot;jdbc/OracleDB&quot; 이름을 찾아 DataSource 객체를 얻어온다.
        DataSource ds = (DataSource) initContext.lookup(&quot;java:/comp/env/jdbc/OracleDB&quot;);
        conn = ds.getConnection(); // DataSource를 통해 풀에서 커넥션을 대여한다.
    } catch (Exception e) {
        e.printStackTrace();
    }
    return conn;
}</code></pre>
<p>DAO 패턴과 커넥션 풀의 조합은 현대적인 웹 애플리케이션 구축의 표준적인 방법이다.</p>
<hr>
<h2 id="회원가입-및-로그인-기능-구현">회원가입 및 로그인 기능 구현</h2>
<p>JSP/Servlet 기반 MVC 패턴을 사용하여 회원가입 및 로그인 기능을 구현하는 코드이다.</p>
<h3 id="db-테이블">DB 테이블</h3>
<p>데이터베이스에 회원 정보를 저장할 <code>member</code> 테이블을 생성한다.</p>
<pre><code class="language-sql">-- member 테이블 생성 SQL
CREATE TABLE member (
    id VARCHAR2(20) PRIMARY KEY,
    password VARCHAR2(20) NOT NULL,
    name VARCHAR2(30) NOT NULL,
    email VARCHAR2(50),
    regdate DATE DEFAULT SYSDATE
);</code></pre>
<h3 id="model-데이터-및-로직-처리">Model (데이터 및 로직 처리)</h3>
<p><strong><code>Member.java</code> (DTO)</strong></p>
<p>회원 정보를 담아 계층 간에 전달하는 객체. 자바빈 규약에 따라 작성한다.</p>
<pre><code class="language-java">// Member.java
package com.example.model;

import java.io.Serializable;
import java.sql.Date;

public class Member implements Serializable {
    private static final long serialVersionUID = 1L;

    // 필드 (DB 컬럼과 일치)
    private String id;
    private String password;
    private String name;
    private String email;
    private Date regdate;

    // 기본 생성자
    public Member() {
    }

    // Getter and Setter
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    public Date getRegdate() { return regdate; }
    public void setRegdate(Date regdate) { this.regdate = regdate; }
}</code></pre>
<br>

<p><strong><code>MemberDAO.java</code> (DAO)</strong></p>
<p>실제 데이터베이스에 접근하여 CRUD 작업을 수행하는 객체. </p>
<p>커넥션 풀을 사용한다.</p>
<pre><code class="language-java">// MemberDAO.java
package com.example.model;

import java.sql.*;
import javax.naming.*;
import javax.sql.*;

public class MemberDAO {

    private DataSource dataSource;

    // 생성자에서 커넥션 풀(DataSource)을 찾아 초기화
    public MemberDAO() {
        try {
            Context context = new InitialContext();
            dataSource = (DataSource) context.lookup(&quot;java:comp/env/jdbc/OracleDB&quot;);
        } catch (NamingException e) {
            e.printStackTrace();
        }
    }

    // 회원가입 (Create)
    public int join(Member member) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        int result = 0;
        String sql = &quot;INSERT INTO member (id, password, name, email) VALUES (?, ?, ?, ?)&quot;;

        try {
            conn = dataSource.getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, member.getId());
            pstmt.setString(2, member.getPassword());
            pstmt.setString(3, member.getName());
            pstmt.setString(4, member.getEmail());
            result = pstmt.executeUpdate(); // 성공 시 1 반환
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (pstmt != null) pstmt.close();
                if (conn != null) conn.close(); // 커넥션을 풀에 반납
            } catch (SQLException e) {}
        }
        return result;
    }

    // 로그인 인증 (Read)
    public int loginCheck(String id, String password) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        int result = -1; // -1: 에러, 0: 비밀번호 불일치, 1: 성공
        String sql = &quot;SELECT password FROM member WHERE id = ?&quot;;

        try {
            conn = dataSource.getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, id);
            rs = pstmt.executeQuery();

            if (rs.next()) { // 아이디가 존재할 경우
                if (rs.getString(&quot;password&quot;).equals(password)) {
                    result = 1; // 로그인 성공
                } else {
                    result = 0; // 비밀번호 불일치
                }
            } else {
                result = -1; // 아이디 존재 안함
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (rs != null) rs.close();
                if (pstmt != null) pstmt.close();
                if (conn != null) conn.close();
            } catch (SQLException e) {}
        }
        return result;
    }
}</code></pre>
<h3 id="view-사용자-인터페이스">View (사용자 인터페이스)</h3>
<p><strong><code>join.jsp</code></strong></p>
<pre><code class="language-java">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;회원가입&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h2&gt;회원가입&lt;/h2&gt;
    &lt;form action=&quot;join.do&quot; method=&quot;post&quot;&gt;
        &lt;label for=&quot;id&quot;&gt;아이디:&lt;/label&gt;&lt;br&gt;
        &lt;input type=&quot;text&quot; id=&quot;id&quot; name=&quot;id&quot; required&gt;&lt;br&gt;&lt;br&gt;
        &lt;label for=&quot;password&quot;&gt;비밀번호:&lt;/label&gt;&lt;br&gt;
        &lt;input type=&quot;password&quot; id=&quot;password&quot; name=&quot;password&quot; required&gt;&lt;br&gt;&lt;br&gt;
        &lt;label for=&quot;name&quot;&gt;이름:&lt;/label&gt;&lt;br&gt;
        &lt;input type=&quot;text&quot; id=&quot;name&quot; name=&quot;name&quot; required&gt;&lt;br&gt;&lt;br&gt;
        &lt;label for=&quot;email&quot;&gt;이메일:&lt;/label&gt;&lt;br&gt;
        &lt;input type=&quot;email&quot; id=&quot;email&quot; name=&quot;email&quot;&gt;&lt;br&gt;&lt;br&gt;
        &lt;input type=&quot;submit&quot; value=&quot;가입하기&quot;&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<br>


<p><strong><code>login.jsp</code></strong></p>
<pre><code class="language-java">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;로그인&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h2&gt;로그인&lt;/h2&gt;
    &lt;form action=&quot;login.do&quot; method=&quot;post&quot;&gt;
        &lt;label for=&quot;id&quot;&gt;아이디:&lt;/label&gt;&lt;br&gt;
        &lt;input type=&quot;text&quot; id=&quot;id&quot; name=&quot;id&quot; required&gt;&lt;br&gt;&lt;br&gt;
        &lt;label for=&quot;password&quot;&gt;비밀번호:&lt;/label&gt;&lt;br&gt;
        &lt;input type=&quot;password&quot; id=&quot;password&quot; name=&quot;password&quot; required&gt;&lt;br&gt;&lt;br&gt;
        &lt;input type=&quot;submit&quot; value=&quot;로그인&quot;&gt;
    &lt;/form&gt;
    &lt;br&gt;
    &lt;%-- 로그인 실패 시 에러 메시지 출력 --%&gt;
    &lt;%
        String error = (String) request.getAttribute(&quot;error&quot;);
        if (error != null) {
    %&gt;
        &lt;p style=&quot;color: red;&quot;&gt;&lt;%= error %&gt;&lt;/p&gt;
    &lt;%
        }
    %&gt;
    &lt;a href=&quot;join.jsp&quot;&gt;회원가입&lt;/a&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<br>


<p><strong><code>main.jsp</code></strong></p>
<pre><code class="language-java">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%
    // 페이지 상단에서 세션 검사
    String userId = (String) session.getAttribute(&quot;userId&quot;);
    if (userId == null) { // 세션에 로그인 정보가 없으면
        response.sendRedirect(&quot;login.jsp&quot;); // 로그인 페이지로 강제 이동
        return; // 현재 페이지의 나머지 코드 실행 중단
    }
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;메인 페이지&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;&lt;%= userId %&gt;님, 환영합니다!&lt;/h1&gt;
    &lt;p&gt;성공적으로 로그인되었습니다.&lt;/p&gt;
    &lt;a href=&quot;logout.do&quot;&gt;로그아웃&lt;/a&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3 id="controller-요청-처리-및-흐름-제어">Controller (요청 처리 및 흐름 제어)</h3>
<p><strong><code>JoinServlet.java</code></strong></p>
<pre><code class="language-java">// JoinServlet.java
package com.example.controller;

import java.io.IOException;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;

import com.example.model.Member;
import com.example.model.MemberDAO;

@WebServlet(&quot;/join.do&quot;)
public class JoinServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 파라미터 인코딩 설정
        request.setCharacterEncoding(&quot;UTF-8&quot;);

        // 2. 파라미터 추출 및 Member 객체 생성
        Member member = new Member();
        member.setId(request.getParameter(&quot;id&quot;));
        member.setPassword(request.getParameter(&quot;password&quot;));
        member.setName(request.getParameter(&quot;name&quot;));
        member.setEmail(request.getParameter(&quot;email&quot;));

        // 3. DAO를 통해 DB에 회원 정보 저장
        MemberDAO dao = new MemberDAO();
        int result = dao.join(member);

        // 4. 결과에 따른 페이지 이동
        if (result == 1) { // 회원가입 성공
            response.sendRedirect(&quot;login.jsp&quot;);
        } else { // 회원가입 실패
            // 실제로는 에러 페이지로 보내거나 알림을 띄우는 것이 좋음
            response.sendRedirect(&quot;join.jsp&quot;);
        }
    }
}</code></pre>
<br>


<p><strong><code>LoginServlet.java</code></strong></p>
<pre><code class="language-java">// LoginServlet.java
package com.example.controller;

import java.io.IOException;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;

import com.example.model.MemberDAO;

@WebServlet(&quot;/login.do&quot;)
public class LoginServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 파라미터 추출
        String id = request.getParameter(&quot;id&quot;);
        String password = request.getParameter(&quot;password&quot;);

        // 2. DAO를 통해 인증 시도
        MemberDAO dao = new MemberDAO();
        int result = dao.loginCheck(id, password);

        // 3. 결과에 따른 처리
        if (result == 1) { // 로그인 성공
            // 세션 생성 및 사용자 정보 저장
            HttpSession session = request.getSession();
            session.setAttribute(&quot;userId&quot;, id);
            response.sendRedirect(&quot;main.jsp&quot;);
        } else { // 로그인 실패
            // 에러 메시지를 request에 저장
            request.setAttribute(&quot;error&quot;, &quot;아이디 또는 비밀번호가 올바르지 않습니다.&quot;);
            // 포워드를 통해 login.jsp로 이동하여 에러 메시지 출력
            RequestDispatcher dispatcher = request.getRequestDispatcher(&quot;login.jsp&quot;);
            dispatcher.forward(request, response);
        }
    }
}</code></pre>
<br>


<p><strong><code>LogoutServlet.java</code></strong></p>
<pre><code class="language-java">// LogoutServlet.java
package com.example.controller;

import java.io.IOException;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;

@WebServlet(&quot;/logout.do&quot;)
public class LogoutServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 세션 가져오기 (없으면 null 반환)
        HttpSession session = request.getSession(false);

        // 2. 세션이 존재하면 무효화
        if (session != null) {
            session.invalidate();
        }

        // 3. 로그인 페이지로 리다이렉트
        response.sendRedirect(&quot;login.jsp&quot;);
    }
}</code></pre>
<hr>
<p>이처럼 JSP/Servlet 기반 MVC 웹 애플리케이션에서는 각 구성 요소가 명확한 역할을 수행한다.</p>
<p><strong>요청은 컨트롤러(서블릿)</strong>가 받아서 흐름을 제어한다.
<strong>데이터 처리와 비즈니스 로직은 모델(DAO, DTO)</strong>이 담당한다.
<strong>화면 표시는 뷰(JSP)</strong>가 담당한다.</p>
<p>이러한 역할 분담은 코드의 재사용성을 높이고 유지보수를 용이하게 만든다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JSP] JavaBean & useBean & EL]]></title>
            <link>https://velog.io/@kt_gml/JSP-JavaBean-useBean-EL</link>
            <guid>https://velog.io/@kt_gml/JSP-JavaBean-useBean-EL</guid>
            <pubDate>Thu, 10 Jul 2025 15:16:10 GMT</pubDate>
            <description><![CDATA[<p><strong>JSP 페이지</strong> 안에서 <code>&lt;% ... %&gt;</code> 스크립틀릿으로 자바 코드를 직접 작성하는 것은, 코드가 길어질수록 가독성과 유지보수성을 급격히 떨어뜨린다. </p>
<p>특히** HTML나 JSP 폼(form)**에서 넘어온 많은 파라미터를 처리하는 코드는 순식간에 지저분해지고 관리가 어렵다.</p>
<pre><code class="language-java">&lt;%
    // 스크립틀릿을 사용한 지저분한 코드의 예
    request.setCharacterEncoding(&quot;UTF-8&quot;);
    String id = request.getParameter(&quot;id&quot;);
    String password = request.getParameter(&quot;password&quot;);
    String name = request.getParameter(&quot;name&quot;);
    String email = request.getParameter(&quot;email&quot;);

    // 넘어온 파라미터마다 객체를 생성하고 setter를 호출해야 한다.
    Person p = new Person(); 
    p.setId(id);
    p.setPassword(password);
    p.setName(name);
    p.setEmail(email);
%&gt;</code></pre>
<p>JSP의 본래 목적인 <strong>&#39;화면 표현(View)&#39;</strong>에 집중하기 위해, 이러한 자바 로직 코드를 분리할 필요가 있다. </p>
<p>이때 JSP <strong>액션 태그(Action Tag)</strong> 중 하나인 <strong><code>&lt;jsp:useBean&gt;</code></strong>이 강력한 해결책이 되어준다.</p>
<hr>
<h2 id="자바빈javabean이란-">자바빈(JavaBean)이란 ?</h2>
<p><code>&lt;jsp:useBean&gt;</code>을 사용하려면, 데이터를 담을 자바 객체가 특정 규칙을 따라야 한다. </p>
<p>이러한 객체를 <strong>자바빈(JavaBean)</strong>이라고 부른다.</p>
<p><strong>자바빈의 규칙</strong></p>
<ol>
<li>클래스는 <code>public</code>으로 선언되어야 한다.</li>
<li>멤버 변수(프로퍼티)는 <code>private</code>으로 선언하여 외부 접근을 막는다.</li>
<li>각 멤버 변수에 접근할 수 있는 <code>public</code> 접근자인 <strong><code>getter/setter</code></strong> 메서드가 있어야 한다. </li>
<li>인자(매개변수)가 없는 <strong>기본 생성자</strong>가 반드시 있어야 한다.</li>
</ol>
<h3 id="ex-personjava">EX: Person.java</h3>
<pre><code class="language-java">package com.example.bean; // 패키지 경로

// 1. public 클래스
public class Person {

    // 2. private 멤버 변수
    private String id;
    private String name;

    // 4. 기본 생성자
    public Person() {
    }

    // 3. public Getter/Setter 메서드
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}</code></pre>
<blockquote>
<p><a href="https://all-record.tistory.com/102">ECLIPSE에서 Getter Setter 쉽게 설정하는 법</a></p>
</blockquote>
<hr>
<h2 id="jspusebean이란"><code>&lt;jsp:useBean&gt;</code>이란?</h2>
<p><strong><code>&lt;jsp:useBean&gt;</code></strong>은 JSP 페이지에서 사용할 <strong>자바빈(JavaBean) 객체를 선언, 생성, 관리</strong>해주는 액션 태그이다. </p>
<p>스크립틀릿의 복잡한 코드를 아래와 같이 간결한 태그 몇 줄로 완벽하게 대체할 수 있다.</p>
<p><strong><code>&lt;jsp:useBean&gt;</code></strong>
자바빈 객체를 생성하거나, 지정된 scope에 이미 객체가 존재하면 그 객체를 가져온다.</p>
<p><strong><code>&lt;jsp:setProperty&gt;</code></strong>
<code>useBean</code>으로 준비된 객체의 필드(프로퍼티)에 값을 자동으로 설정한다.</p>
<p><strong><code>&lt;jsp:getProperty&gt;</code></strong> 
객체 필드의 값을 가져와 화면에 출력한다.</p>
<h3 id="usebean-사용-예-before--after">useBean 사용 예 (Before &amp; After)</h3>
<p><strong><code>personAction.jsp</code></strong></p>
<h3 id="before-스크립틀릿-사용"><strong>Before: 스크립틀릿 사용</strong></h3>
<pre><code class="language-java">&lt;%@ page import=&quot;com.example.bean.Person&quot; %&gt;
&lt;%
    request.setCharacterEncoding(&quot;UTF-8&quot;);

    Person p = new Person(); // 직접 객체 생성
    p.setId(request.getParameter(&quot;id&quot;)); // 파라미터 하나하나 직접 설정
    p.setName(request.getParameter(&quot;name&quot;));
%&gt;
아이디: &lt;%= p.getId() %&gt; &lt;br&gt;
이름: &lt;%= p.getName() %&gt;</code></pre>
<h4 id="after-jspusebean-액션-태그-사용"><strong>After: <code>&lt;jsp:useBean&gt;</code> 액션 태그 사용</strong></h4>
<pre><code class="language-java">&lt;%@ page contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;% request.setCharacterEncoding(&quot;UTF-8&quot;); %&gt;

&lt;%-- 1. Person 객체를 request 영역에서 찾거나, 없으면 새로 생성하여 &#39;p&#39;라는 이름으로 사용 --%&gt;
&lt;jsp:useBean id=&quot;p&quot; class=&quot;com.example.bean.Person&quot; scope=&quot;request&quot; /&gt;

&lt;%-- 2. p 객체의 프로퍼티(id, name)에 요청 파라미터 값을 자동으로 설정 --%&gt;
&lt;jsp:setProperty name=&quot;p&quot; property=&quot;*&quot; /&gt;

아이디: &lt;jsp:getProperty name=&quot;p&quot; property=&quot;id&quot; /&gt; &lt;br&gt;
이름: &lt;jsp:getProperty name=&quot;p&quot; property=&quot;name&quot; /&gt;</code></pre>
<h4 id="액션-태그-자세히-보기">액션 태그 자세히 보기</h4>
<p><strong><code>&lt;jsp:useBean id=&quot;p&quot; class=&quot;com.example.bean.Person&quot; scope=&quot;request&quot; /&gt;</code></strong></p>
<p><code>id</code>: JSP 페이지 내에서 사용할 객체의 변수 이름(<code>p</code>)을 지정한다.</p>
<p><code>class</code>: 사용할 자바빈의 전체 클래스 경로(패키지 포함)를 지정한다.</p>
<p><code>scope</code>: 객체가 저장되고 유지될 메모리 영역을 지정한다. 
(<code>page</code>, <code>request</code>, <code>session</code>, <code>application</code> 중 선택)
<br>
<strong><code>&lt;jsp:setProperty name=&quot;p&quot; property=&quot;*&quot; /&gt;</code></strong></p>
<p><code>name</code>: 값을 설정할 자바빈 객체의 <code>id</code>를 지정한다. 
(<code>useBean</code>의 <code>id</code>와 일치)</p>
<p><code>property=&quot;*&quot;</code>** 
요청 파라미터의 이름과 자바빈의 프로퍼티(멤버 변수) 이름이 일치하는 모든 항목에 대해, 해당 <code>setter</code> 메서드(<code>setId()</code>, <code>setName()</code> 등)를 <strong>자동으로 호출하여 값을 설정</strong>해준다.</p>
<hr>
<h2 id="왜-jspusebean을-사용해야-할까">왜 <code>&lt;jsp:useBean&gt;</code>을 사용해야 할까?</h2>
<p><code>&lt;jsp:useBean&gt;</code> 액션 태그를 사용하면 JSP 페이지에서 <strong>데이터 처리 로직(Java 코드)을 거의 완벽하게 제거</strong>할 수 있다. </p>
<p>이를 통해 몇가지 이점을 얻는다.</p>
<ol>
<li><strong>가독성 및 유지보수성 향상</strong>: HTML과 유사한 태그로 로직을 처리하므로 코드가 훨씬 깔끔하고 이해하기 쉬워집니다.</li>
<li><strong>역할 분리</strong>: JSP는 화면을 그리는 역할에, 자바빈은 데이터를 담는 역할에 집중할 수 있어 웹 애플리케이션의 구조가 명확해집니다.</li>
<li><strong>생산성 증가</strong>: <code>property=&quot;*&quot;</code> 속성 하나로 수많은 <code>request.getParameter()</code>와 <code>setter</code> 호출 코드를 대체하여 개발 시간을 단축시킵니다.</li>
</ol>
<p>이때 <code>&lt;jsp:getProperty&gt;</code>보다 훨씬 직관적이고 간결하게 객체의 값을 출력하는 방법이 있다.</p>
<p>바로 <strong>표현 언어(Expression Language, EL)</strong>를 사용하는 것이다.</p>
<p><code>{p.id}</code>와 같은 형태로 값을 가져오는 방법이 바로 EL의 기본 문법이다.</p>
<hr>
<h2 id="표현-언어el란">표현 언어(EL)란?</h2>
<p>EL은 JSP 2.0부터 기본으로 포함된 기능으로, JSP 페이지에서 <strong>데이터에 더 쉽게 접근하기 위해 만들어진 언어</strong>이다. </p>
<p><code>$</code> 기호와 중괄호 <code>{}</code>를 사용하여 표현한다. (예: <code>${표현식}</code>)</p>
<p>EL을 사용하면 <code>&lt;jsp:getProperty&gt;</code>나 스크립틀릿 표현식(<code>&lt;%= ... %&gt;</code>) 없이도 자바빈의 프로퍼티, 컬렉션, 배열 등의 값에 간단하게 접근할 수 있다.</p>
<h2 id="el을-사용한-개선-방법">EL을 사용한 개선 방법</h2>
<h3 id="before-jspgetproperty-사용"><strong>Before: <code>&lt;jsp:getProperty&gt;</code> 사용</strong></h3>
<pre><code class="language-jsp">아이디: &lt;jsp:getProperty name=&quot;p&quot; property=&quot;id&quot; /&gt; &lt;br&gt;
이름: &lt;jsp:getProperty name=&quot;p&quot; property=&quot;name&quot; /&gt;</code></pre>
<h3 id="after-표현-언어el-사용"><strong>After: 표현 언어(EL) 사용</strong></h3>
<pre><code class="language-jsp">아이디: ${p.id} &lt;br&gt;
이름: ${p.name}</code></pre>
<p>코드가 훨씬 간결하고 직관적으로 변한 것을 바로 확인할 수 있다. </p>
<p>또 자바스크립트에서 객체 속성에 접근하는 것과 유사하여 JS를 접해봤다면 이해하기도 쉽다.</p>
<hr>
<h2 id="el의-동작원리">EL의 동작원리</h2>
<ol>
<li><p><strong>객체 저장</strong>
<code>&lt;jsp:useBean id=&quot;p&quot; ... scope=&quot;request&quot; /&gt;</code> 태그는 <code>Person</code> 객체를 생성하여 <code>request</code>라는 메모리 영역(scope)에 <code>p</code>라는 이름으로 저장한다.</p>
</li>
<li><p><strong>객체 검색</strong> 
EL 표현식 <code>${p.id}</code>가 실행되면, JSP 컨테이너는 다음 순서로 <code>p</code>라는 이름의 객체(Attribute)를 자동으로 찾는다.
<code>page</code> 영역 -&gt; <code>request</code> 영역 -&gt; <code>session</code> 영역 -&gt;<code>application</code> 영역</p>
</li>
<li><p><strong>Getter 호출</strong>
<code>request</code> 영역에서 <code>p</code> 객체를 찾으면, EL은 <code>p.id</code> 부분을 해석하여 <code>p.getId()</code> 메서드를 자동으로 호출하고 그 반환값을 해당 위치에 출력한다.</p>
</li>
</ol>
<hr>
<h2 id="최종-완성-코드-usebean--setproperty--el">최종 완성 코드 (useBean + setProperty + EL)</h2>
<p>이 세 가지를 조합하면 가장 이상적이고 현대적인 JSP 코드가 완성된다.</p>
<pre><code class="language-java">&lt;%@ page contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;% request.setCharacterEncoding(&quot;UTF-8&quot;); %&gt;

&lt;%-- 1. useBean으로 Person 객체를 request 영역에 준비 --%&gt;
&lt;jsp:useBean id=&quot;p&quot; class=&quot;com.example.bean.Person&quot; scope=&quot;request&quot; /&gt;

&lt;%-- 2. setProperty로 폼 파라미터 값을 객체에 자동으로 채우기 --%&gt;
&lt;jsp:setProperty name=&quot;p&quot; property=&quot;*&quot; /&gt;

&lt;h3&gt;회원 정보 (EL 사용으로 깔끔해진 코드)&lt;/h3&gt;
&lt;ul&gt;
    &lt;li&gt;아이디: ${p.id}&lt;/li&gt;
    &lt;li&gt;이름: ${p.name}&lt;/li&gt;
&lt;/ul&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JSP / WEB] 쿠키(Cookie)와 세션(Session)]]></title>
            <link>https://velog.io/@kt_gml/JSP-%EC%BF%A0%ED%82%A4Cookie%EC%99%80-%EC%84%B8%EC%85%98Session</link>
            <guid>https://velog.io/@kt_gml/JSP-%EC%BF%A0%ED%82%A4Cookie%EC%99%80-%EC%84%B8%EC%85%98Session</guid>
            <pubDate>Thu, 10 Jul 2025 03:05:24 GMT</pubDate>
            <description><![CDATA[<h1 id="cookie--session">Cookie &amp; Session</h1>
<h2 id="쿠키cookie">쿠키(Cookie)</h2>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/606c54dd-a91f-4521-9981-dc304ebdb4aa/image.png" alt=""></p>
<p>HTTP 프로토콜은 &#39;상태가 없는(Stateless)&#39; 특징을 가진다. </p>
<p>이는 서버가 각 요청을 독립적인 것으로 취급하여 이전 요청을 기억하지 못함을 의미한다. </p>
<p>&quot;방금 로그인한 사용자가 누구인지&quot; 알 수 없는 것이다. </p>
<p>이 문제를 해결하기 위해, 서버는 브라우저에 작은 정보를 남겨두는데 이것이 바로 <strong>쿠키(Cookie)</strong>다.</p>
<p>쿠키는 서버가 사용자의 <strong>브라우저(클라이언트)에 저장하는 작은 텍스트 조각</strong>이다. </p>
<p>브라우저는 서버에 요청을 보낼 때마다 이 쿠키를 함께 전송하여, 서버가 사용자를 식별할 수 있도록 돕는다.</p>
<hr>
<h3 id="쿠키의-주요-역할">쿠키의 주요 역할</h3>
<p><strong>상태 관리</strong> 
사용자의 로그인 상태를 유지(자동로그인)한다.
비로그인 상태의 장바구니를 관리한다.</p>
<p><strong>사용자 추적</strong> 
사용자의 방문 기록이나 행동 패턴을 분석하는 데 사용된다.</p>
<p><strong>개인화</strong> 
&quot;오늘 하루 이 창을 열지 않음&quot; 팝업, 언어 설정 등 사용자 맞춤형 경험을 제공한다.</p>
<hr>
<h3 id="jsp-쿠키-객체cookie-활용법">JSP 쿠키 객체(Cookie) 활용법</h3>
<p>JSP에서 쿠키를 다루는 방법은 매우 간단하다.</p>
<p><strong>1. 쿠키 생성 및 전송</strong>
<code>response</code> 내장 객체를 사용한다.</p>
<pre><code class="language-java">&lt;%
    // 1. &quot;userId&quot;라는 이름에 &quot;guest&quot;라는 값을 가진 쿠키 객체 생성
    Cookie cookie = new Cookie(&quot;userId&quot;, &quot;guest&quot;);

    // 2. 쿠키 유효기간 설정 (초 단위, 여기서는 24시간)
    cookie.setMaxAge(60 * 60 * 24);

    // 3. response 객체에 쿠키를 추가하여 클라이언트로 전송
    response.addCookie(cookie);
%&gt;</code></pre>
<p><strong>2. 쿠키 읽기</strong>
<code>request</code> 내장 객체를 사용한다. 
브라우저는 여러 쿠키를 가질 수 있으므로 배열 형태로 받아온다.</p>
<pre><code class="language-java">&lt;%
    // 1. request로부터 모든 쿠키를 배열로 가져온다.
    Cookie[] cookies = request.getCookies();
    String userId = &quot;&quot;;

    if (cookies != null) {
        // 2. 쿠키 배열을 순회하며 원하는 이름의 쿠키를 찾는다.
        for (Cookie c : cookies) {
            if (c.getName().equals(&quot;userId&quot;)) {
                userId = c.getValue();
                break; // 찾았으면 반복 종료
            }
        }
    }
%&gt;
&lt;p&gt;현재 사용자 ID는 &lt;%= userId %&gt; 입니다.&lt;/p&gt;</code></pre>
<p><strong>3. 쿠키 삭제</strong>
쿠키를 직접 삭제하는 메서드는 없다. 
대신, <strong>유효기간을 0으로 설정</strong>하여 브라우저가 즉시 삭제하도록 유도할 수 있다.</p>
<pre><code class="language-java">&lt;%
    Cookie cookie = new Cookie(&quot;userId&quot;, &quot;&quot;); // 값은 비워도 무방하다.
    cookie.setMaxAge(0); // 유효기간을 0으로 설정
    response.addCookie(cookie);
%&gt;</code></pre>
<p>쿠키는 클라이언트 측에 데이터를 저장하는 간단하고 유용한 방법이다.</p>
<p>하지만 보안에 민감한 정보나 큰 데이터를 저장하기에는 적합하지 않다. </p>
<p>이러한 단점을 보완하기 위해 서버 측 저장 방식인 <strong>세션</strong>을 사용한다.</p>
<hr>
<h2 id="세션session">세션(Session)</h2>
<p>쿠키는 브라우저에 저장되어 보안에 취약하고 저장 데이터의 양도 제한적이다. </p>
<p>로그인 정보나 장바구니처럼 중요하고 복잡한 정보는 <strong>세션(Session)</strong>을 이용해 처리해야 한다.</p>
<p>세션은 쿠키와 달리 <strong>데이터를 서버 쪽에 저장</strong>하는 방식이다. </p>
<p>서버는 각 클라이언트를 위한 고유한 저장 공간(세션)을 만들고, 클라이언트에게는 그 공간에 접근할 수 있는 <strong>&#39;열쇠(ID)&#39;</strong>만 쿠키 형태로 전달한다. </p>
<p>클라이언트는 요청 시마다 이 열쇠를 함께 보내고, 서버는 열쇠를 보고 맞는 세션 저장소의 데이터를 사용한다.</p>
<h3 id="세션의-동작-원리">세션의 동작 원리</h3>
<ol>
<li><p><strong>최초 접속</strong> 
클라이언트가 서버에 처음 접속하면, 서버는 고유한 <strong>세션 ID</strong>를 생성한다.</p>
</li>
<li><p><strong>세션 저장소 생성</strong> 
서버 메모리에 해당 세션 ID와 연결되는 저장 공간을 만든다.</p>
</li>
<li><p><strong>세션 ID 전송</strong> 
서버는 생성한 세션 ID를 클라이언트 브라우저에 <strong>쿠키</strong>(보통 <code>JSESSIONID</code> 이름) 형태로 저장시킨다.</p>
</li>
<li><p><strong>재접속</strong> 
클라이언트가 다시 요청하면, 브라우저는 자동으로 세션 ID 쿠키를 함께 보낸다.</p>
</li>
<li><p><strong>세션 데이터 활용</strong> 
서버는 전달받은 세션 ID를 통해 서버에 저장된 해당 클라이언트의 데이터를 찾아 사용한다.</p>
</li>
</ol>
<h3 id="jsp-세션session-내장-객체-활용법">JSP 세션(session) 내장 객체 활용법</h3>
<p>JSP는 세션을 편리하게 다루도록 <code>session</code>이라는 내장 객체를 기본으로 제공한다.</p>
<p><strong>1. 세션에 값 저장하기</strong>
<code>setAttribute(&quot;이름&quot;, 값)</code> 메서드를 사용한다. 값으로는 모든 종류의 객체(Object)를 저장할 수 있다.</p>
<pre><code class="language-java">&lt;%
    String userId = &quot;testuser&quot;;
    // &quot;loginId&quot; 라는 이름으로 userId 변수의 값을 세션에 저장
    session.setAttribute(&quot;loginId&quot;, userId);
%&gt;</code></pre>
<p><strong>2. 세션에서 값 불러오기</strong>
<code>getAttribute(&quot;이름&quot;)</code> 메서드를 사용한다. 
반환 타입이 Object이므로, 원래 타입으로 형 변환(Casting)이 필요하다.</p>
<pre><code class="language-java">&lt;%
    // &quot;loginId&quot; 라는 이름의 세션 값을 가져온다.
    Object value = session.getAttribute(&quot;loginId&quot;);
    String loginId = &quot;&quot;;
    if (value != null) {
        loginId = (String) value;
    }
%&gt;
&lt;p&gt;현재 로그인한 아이디: &lt;%= loginId %&gt;&lt;/p&gt;</code></pre>
<p><strong>3. 세션에서 특정 값 삭제하기</strong>
<code>removeAttribute(&quot;이름&quot;)</code> 메서드를 사용한다.</p>
<pre><code class="language-java">&lt;%
    // &quot;loginId&quot; 세션만 삭제
    session.removeAttribute(&quot;loginId&quot;);
%&gt;</code></pre>
<p><strong>4. 세션 전체 무효화 (로그아웃)</strong>
<code>invalidate()</code> 메서드를 사용한다. 해당 클라이언트의 세션 저장소 전체가 서버에서 삭제된다.</p>
<pre><code class="language-java">&lt;%
    // 세션을 완전히 종료시킨다 (로그아웃 시 사용).
    session.invalidate();
%&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JSP] JSP & Servlet]]></title>
            <link>https://velog.io/@kt_gml/JSP-JSP-Servlet</link>
            <guid>https://velog.io/@kt_gml/JSP-JSP-Servlet</guid>
            <pubDate>Wed, 09 Jul 2025 06:46:30 GMT</pubDate>
            <description><![CDATA[<h2 id="jsp와-servlet-무엇이-다를까-">JSP와 Servlet, 무엇이 다를까 ?</h2>
<p>Java로 웹 개발을 시작하면 가장 먼저 <strong>JSP(JavaServer Pages)</strong>와 <strong>서블릿(Servlet)</strong>을 만나게 된다.</p>
<p><strong>서블릿 (Servlet)</strong>
서블릿은 <strong>순수 자바(Java) 코드</strong>이다. 
서블릿은 사용자의 요청을 처리하고, 비즈니스 로직을 실행하며, 데이터를 제어하는 역할을 수행한다.</p>
<p><strong>JSP (JavaServer Pages)</strong>
JSP는 <strong>HTML 코드 안에 자바 코드를 삽입하여 사용한다.</strong> 
JSP는 사용자에게 최종적으로 보여질 화면의 구조와 내용을 담는다.</p>
<hr>
<h3 id="서블릿의-한계">서블릿의 한계</h3>
<p>초기 웹 개발에서는 서블릿만으로 모든 것을 처리했다. </p>
<pre><code class="language-java">out.println(&quot;&lt;html&gt;&lt;body&gt;&lt;h1&gt;안녕하세요&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;&quot;);</code></pre>
<p>위의 코드처럼 자바만을 이용해 한 줄 한 줄 HTML을 문자열로 출력하는 것은 비효율적이었다. </p>
<p>디자인을 조금만 변경해도 자바 코드를 다시 컴파일해야 하는 번거로움도 있었다.</p>
<p>이처럼 서블릿은 <strong>로직 처리에는 강력하지만, 화면을 구성하는 표현(Presentation)에는 매우 취약한 구조</strong>를 가졌다.</p>
<hr>
<h3 id="jsp--html-문서에-자바를-심다">JSP : HTML 문서에 자바를 심다</h3>
<p>이러한 불편을 해결하기 위해 JSP가 등장했다. </p>
<p>JSP는 반대로 HTML을 기본 구조로 삼고, 필요한 부분에만 자바 코드를 넣어 동적으로 데이터를 처리할 수 있도록 했다. </p>
<p>이로써 디자이너는 자바를 몰라도 HTML 구조를 쉽게 수정하고, 개발자는 필요한 부분에만 로직을 추가하는 역할 분담이 가능해졌다.</p>
<hr>
<h3 id="mvc-패턴">MVC 패턴</h3>
<blockquote>
<p><a href="https://velog.io/@kt_gml/JSP-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B5%AC%EC%A1%B0MVC-%ED%8C%A8%ED%84%B4">MVC 패턴 및 JSP 프로젝트 구조</a></p>
</blockquote>
<p>결론적으로 현대 웹 개발은 이 둘을 함께 사용하는 <strong>MVC(Model-View-Controller)</strong> 패턴으로 정착되었다.</p>
<p><strong>Controller(컨트롤러) : 서블릿</strong>
사용자의 모든 요청을 가장 먼저 받는다.
필요한 비즈니스 로직을 처리하고 데이터를 가공한다.
그 결과를 어떤 JSP에게 보여줄지 결정하여 전달한다.</p>
<p><strong>View(뷰) : JSP</strong>
컨트롤러(서블릿)로부터 전달받은 데이터를 화면에 그리는 역할에만 집중한다.</p>
<p><strong>Model(모델)</strong> 
데이터 그 자체나 데이터를 처리하는 자바 클래스(JavaBean, DTO, DAO 등)를 의미한다.</p>
<p><strong>흐름</strong> 
사용자 요청 → <strong>서블릿</strong>이 받아서 처리 
→ 결과를 <strong>JSP</strong>에 전달 
→ JSP가 화면을 그려서 사용자에게 응답</p>
<h3 id="jsp-핵심-문법-요약">JSP 핵심 문법 요약</h3>
<table>
<thead>
<tr>
<th align="left">구분</th>
<th align="left">문법</th>
<th align="left">설명</th>
<th align="left">예시</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>지시어 (Directive)</strong></td>
<td align="left"><code>&lt;%@ ... %&gt;</code></td>
<td align="left">페이지 전체에 대한 설정 (인코딩, import 등)</td>
<td align="left"><code>&lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; %&gt;</code></td>
</tr>
<tr>
<td align="left"><strong>선언문 (Declaration)</strong></td>
<td align="left"><code>&lt;%! ... %&gt;</code></td>
<td align="left">멤버 변수나 메서드를 선언</td>
<td align="left"><code>&lt;%! int count = 0; %&gt;</code></td>
</tr>
<tr>
<td align="left"><strong>스크립틀릿 (Scriptlet)</strong></td>
<td align="left"><code>&lt;% ... %&gt;</code></td>
<td align="left">자바 로직 코드를 작성 (가장 많이 사용)</td>
<td align="left"><code>&lt;% for(int i=0; i&lt;5; i++) { ... } %&gt;</code></td>
</tr>
<tr>
<td align="left"><strong>표현식 (Expression)</strong></td>
<td align="left"><code>&lt;%= ... %&gt;</code></td>
<td align="left">변수 값이나 메서드 리턴 값을 출력 (<code>;</code> 사용 안 함)</td>
<td align="left"><code>안녕하세요, &lt;%= name %&gt;님!</code></td>
</tr>
<tr>
<td align="left"><strong>주석 (Comment)</strong></td>
<td align="left"><code>&lt;%-- ... --%&gt;</code></td>
<td align="left">JSP 주석 (HTML 소스 보기에도 나타나지 않음)</td>
<td align="left"><code>&lt;%-- 이 부분은 클라이언트에게 보이지 않는다. --%&gt;</code></td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JSP] 프로젝트 구조(MVC 패턴)]]></title>
            <link>https://velog.io/@kt_gml/JSP-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B5%AC%EC%A1%B0MVC-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@kt_gml/JSP-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B5%AC%EC%A1%B0MVC-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Mon, 07 Jul 2025 14:34:39 GMT</pubDate>
            <description><![CDATA[<h1 id="프로젝트-구조-왜-중요할까">프로젝트 구조, 왜 중요할까?</h1>
<p>나도 그랬듯이 초보자들은 프로젝트를 시작할 때, 모든 코드를 파일 하나에 다 넣으려고 한다. </p>
<p>하지만 JSP를 예로 들었을때 HTML, CSS, JavaScript, 자바 로직, 데이터베이스 연결 코드까지 한 파일에 뒤섞이면 처음에는 빠르게 결과물이 나오는 것처럼 보인다. </p>
<p>하지만 프로젝트가 조금만 커져도, 그 코드는 누구도 손대고 싶지 않은 <strong>&#39;스파게티 코드&#39;</strong>가 되어버린다.</p>
<p>잘못된 프로젝트 구조는 유지보수를 악몽으로 만들고, 팀원과의 협업을 불가능하게 한다. </p>
<p>프로젝트에서 왜 구조가 중요한지, 그리고 가장 표준적으로 사용되는 <strong>MVC 패턴</strong>을 기반으로 어떻게 깔끔한 구조를 만들 수 있는지 설명하겠다.</p>
<hr>
<h2 id="왜-프로젝트-구조가-중요한가">왜 프로젝트 구조가 중요한가?</h2>
<p><strong>역할 분담</strong> 
디자이너는 화면(View)에, 개발자는 로직(Controller, Model)에 집중할 수 있게 한다.</p>
<p><strong>유지보수성</strong> 
&quot;로그인 로직을 수정해야지&quot; 라고 생각했을 때, <code>LoginServlet.java</code>와 <code>MemberDAO.java</code>만 보면 되도록 만들어준다. 
수많은 JSP 파일을 뒤질 필요가 없다.</p>
<p><strong>재사용성</strong> 
잘 만들어진 데이터베이스 처리 로직(<code>MemberDAO</code>)은 회원 정보가 필요한 어떤 페이지에서든 재사용할 수 있다.</p>
<p><strong>협업 효율성</strong> 
모든 팀원이 파일이 어디에 위치하고 어떤 역할을 하는지 예측할 수 있어, 코드 충돌을 줄이고 개발 속도를 높인다.</p>
<hr>
<h2 id="jsp-프로젝트의-표준-구조-mvc-패턴">JSP 프로젝트의 표준 구조: MVC 패턴</h2>
<p>JSP/Servlet 웹 애플리케이션의 구조를 잡는 가장 표준적인 방법은 <strong>MVC 패턴</strong>이다. 이 패턴은 프로젝트를 세 가지 역할로 명확하게 나눈다.</p>
<p><strong>Model</strong> 
데이터 그 자체(DTO/VO)와 데이터를 처리하는 비즈니스 로직(Service, DAO). 순수 자바 클래스로 구성된다.</p>
<p><strong>View</strong> 
사용자에게 보여지는 화면. <strong>JSP 파일</strong>이 이 역할을 전담한다.</p>
<p><strong>Controller</strong> 
사용자의 요청을 받고, 모델과 뷰를 연결하는 중재자. <strong>서블릿(Servlet)</strong>이 이 역할을 맡는다.</p>
<h3 id="예시-기본적인-mvc-프로젝트-폴더-구조">예시: 기본적인 MVC 프로젝트 폴더 구조</h3>
<p>이클립스의 &#39;Dynamic Web Project&#39; 기준으로, 다음과 같은 구조를 따르는 것이 일반적이다.</p>
<pre><code>my-web-project/
├── src/main/java/          # 순수 자바 코드가 위치하는 곳 (Model, Controller)
│   └── com/example/
│       ├── controller/     # 1. Controller (서블릿)
│       │   ├── LoginServlet.java
│       │   └── JoinServlet.java
│       ├── model/          # 2. Model (데이터 관련 클래스)
│       │   ├── dao/        #    - DAO (Data Access Object)
│       │   │   └── MemberDAO.java
│       │   └── dto/        #    - DTO (Data Transfer Object)
│       │       └── MemberDTO.java
│       └── util/           # 3. 유틸리티 클래스
│           └── DBManager.java  (DB 커넥션 풀 관리 등)
│
├── src/main/webapp/        # 웹 콘텐츠가 위치하는 곳 (View)
│   ├── WEB-INF/
│   │   ├── web.xml         #    - 서블릿 매핑 등 웹 애플리케이션 설정 파일
│   │   └── lib/            #    - 외부 라이브러리 (ex: ojdbc.jar)
│   ├── member/             # 기능별로 폴더를 나누면 좋음
│   │   ├── login.jsp
│   │   └── join.jsp
│   ├── main.jsp
│   ├── index.jsp
│   └── css/
│       └── style.css
│
└── pom.xml                 # (Maven 프로젝트인 경우) 의존성 관리 파일</code></pre><h3 id="각-폴더의-역할">각 폴더의 역할</h3>
<ol>
<li><p><strong><code>src/main/java</code>
백엔드 로직의 핵심</strong>
<strong><code>controller</code> 패키지</strong> 
사용자의 모든 <code>HTTP</code> 요청(<code>GET</code>, <code>POST</code>)을 가장 먼저 받는 서블릿들이 위치한다. 서블릿은 요청을 분석해서 어떤 모델을 사용할지, 어떤 뷰를 보여줄지 결정하는 &#39;교통경찰&#39; 역할을 한다.</p>
<p><strong><code>model.dao</code> 패키지</strong> 
<code>MemberDAO</code>처럼 데이터베이스에 직접 접근하여 <code>INSERT</code>, <code>SELECT</code>, <code>UPDATE</code>, <code>DELETE</code> (CRUD) 로직을 전담하는 클래스들이 모여있다.</p>
<p><strong><code>model.dto</code> 패키지</strong> 
<code>MemberDTO</code>처럼 데이터베이스 테이블의 정보를 행(Row) 단위로 담기 위한 객체(JavaBean)들이 위치한다. 계층 간 데이터 전송을 위한 &#39;데이터 그릇&#39;이다.</p>
</li>
<li><p><strong><code>src/main/webapp</code></strong></p>
</li>
</ol>
<p><strong>사용자의 눈에 보이는 모든 것</strong>
    내가 앞으로 공유할 코드들과 나의 Github에 있는 코드들은 교수님께 배운대로 모두 경로를 WebContents 바꿔놨다.</p>
<p>  이클립스 기준으로 프로젝트 생성시에 경로를 바꿀 수 있다.</p>
<p>   이 폴더 아래의 파일들은 URL을 통해 직접 접근이 가능하다. (단, <code>WEB-INF</code> 폴더 제외)</p>
<p>   <strong><code>.jsp</code> 파일 (View)</strong> 
   컨트롤러(서블릿)로부터 전달받은 데이터를 화면에 동적으로 그리는 역할을 한다. 자바 로직은 최소화하고, 화면 표시에만 집중해야 한다.</p>
<p>   <strong><code>css</code>, <code>js</code>, <code>images</code> 폴더</strong> 
   정적인 리소스 파일들을 위치시킨다.</p>
<ol start="3">
<li><strong><code>WEB-INF</code></strong> </li>
</ol>
<p><strong>보안이 필요한 핵심 설정 공간</strong>
    이 폴더 안의 파일들은 클라이언트가 URL로 직접 요청할 수 없어 보안에 유리하다.</p>
<p>   <strong><code>web.xml</code></strong> 
   배포 서술자(Deployment Descriptor)라고 부른다. 어떤 URL 요청을 어떤 서블릿으로 연결할지(서블릿 매핑), 필터, 리스너 등 웹 애플리케이션의 핵심 설정을 정의한다.</p>
<p>   <strong><code>lib</code></strong> 
   <code>jdbc.jar</code> 같은 외부 라이브러리(JAR 파일)를 위치시키는 곳이다.</p>
<h3 id="파일이-함께-동작하는-방식-로그인-과정-예시">파일이 함께 동작하는 방식 (로그인 과정 예시)</h3>
<ol>
<li><p><strong>사용자 요청</strong> 
사용자는 <code>login.jsp</code>에서 아이디와 비밀번호를 입력하고 &#39;로그인&#39; 버튼을 누른다. 폼의 <code>action</code>은 <code>/login</code>을 가리킨다.</p>
</li>
<li><p><strong>Controller (서블릿) 처리</strong> 
<code>web.xml</code>에 <code>/login</code> 요청을 처리하도록 매핑된 <code>LoginServlet</code>이 요청을 가로챈다.</p>
<p><code>LoginServlet</code>은 <code>request.getParameter()</code>로 사용자가 입력한 아이디와 비밀번호를 받는다.</p>
<p><code>MemberDAO</code> 객체를 생성하고, <code>dao.loginCheck(id, pw)</code> 메서드를 호출하여 DB에 해당 사용자가 있는지 확인한다.</p>
</li>
<li><p><strong>Model (DAO, DTO) 작업:</strong>
<code>MemberDAO</code>는 커넥션 풀에서 DB 커넥션을 얻어와 <code>SELECT</code> 쿼리를 실행한다.</p>
<p>결과가 있다면 로그인 성공, 없으면 실패로 판단하고 그 결과를 <code>LoginServlet</code>에 반환한다.</p>
</li>
<li><p><strong>View (JSP)로 전달 및 표시:</strong></p>
<p><strong>로그인 성공 시:</strong> <code>LoginServlet</code>은 <code>session.setAttribute(&quot;userId&quot;, id)</code>로 세션에 로그인 정보를 기록한 뒤, <code>response.sendRedirect(&quot;main.jsp&quot;)</code>를 통해 메인 페이지로 사용자를 보낸다.</p>
<p><strong>로그인 실패 시:</strong> 에러 메시지를 <code>request</code>에 담아 <code>RequestDispatcher</code>를 이용해 다시 <code>login.jsp</code>로 포워딩한다. <code>login.jsp</code>는 전달받은 에러 메시지를 화면에 표시한다.</p>
</li>
</ol>
<hr>
<p>이처럼 역할에 따라 <strong>프로젝트 구조</strong>를 명확히 나누는 것은 선택이 아닌 필수이다. </p>
<p><strong>MVC 패턴</strong>은 복잡한 웹 애플리케이션을 여러 개발자가 협업하여 만들고, 오랫동안 안정적으로 유지보수할 수 있게 하는 가장 검증되고 안정적인 방법이다.</p>
<p>실무에서 쓰이는 개념들을 하나하나 익힐수록 개인 프로젝트도 관리가 편해지고 뭔가 효율적이고 깔끔한 코딩을 하게되는 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Git] Git & GitHub의 개념과 기초 사용법]]></title>
            <link>https://velog.io/@kt_gml/Git-Git-GitHub%EC%9D%98-%EA%B0%9C%EB%85%90%EA%B3%BC-%EA%B8%B0%EC%B4%88-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@kt_gml/Git-Git-GitHub%EC%9D%98-%EA%B0%9C%EB%85%90%EA%B3%BC-%EA%B8%B0%EC%B4%88-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Mon, 07 Jul 2025 07:49:32 GMT</pubDate>
            <description><![CDATA[<h2 id="개발자의-필수-도구-git--github">개발자의 필수 도구, Git &amp; GitHub</h2>
<p><code>최종_보고서.hwp</code>
<code>진짜최종_보고서.hwp</code>
<code>진짜진짜최종_수정본_보고서.hwp</code>
<code>교수님께_보낼파일_최종.hwp</code></p>
<p>우리는 모두 파일의 버전을 관리하느라 고통받아 본 경험이 있다. </p>
<p>개발 프로젝트는 수십, 수백, 수만 개의 파일로 이루어져 있고, 수많은 사람이 동시에 작업한다. </p>
<p>위와 같은 방식으로 파일 버전을 관리하며 이메일로 파일을 주고받기엔 한계가 있다.</p>
<p>이때 사용하는 도구가 바로 <strong>Git</strong>과 <strong>GitHub</strong>이다.</p>
<hr>
<h2 id="git--github-무엇이-다른가">Git &amp; GitHub, 무엇이 다른가?</h2>
<p>가장 먼저 이 둘의 관계를 명확히 해야 한다. </p>
<p><strong>Git은 &#39;도구&#39;이고, GitHub는 그 도구를 활용하는 &#39;서비스&#39;이다.</strong></p>
<blockquote>
<p><strong>Git (소프트웨어):</strong> 버전 관리 <strong>&#39;소프트웨어(도구)&#39;</strong> </p>
</blockquote>
<p>내 컴퓨터에 설치하여 코드의 변경 이력을 사진 찍듯이 저장하고, 언제든 과거로 돌아갈 수 있는 타임머신 역할을 한다. </p>
<p>인터넷 없이도 내 컴퓨터에서 모든 버전 관리가 가능하다.</p>
<blockquote>
<p><strong>GitHub (웹 서비스):</strong> Git으로 관리하는 프로젝트를 올려두는 <strong>&#39;웹 호스팅 서비스&#39;</strong> </p>
</blockquote>
<p>Git이라는 도구만으로는 힘든 <strong>백업, 공유, 협업</strong>을 가능하게 해준다.</p>
<hr>
<h2 id="왜-반드시-함께-사용해야-하는가">왜 반드시 함께 사용해야 하는가?</h2>
<ol>
<li><strong>완벽한 버전 관리 (Git) + 안전한 클라우드 백업 (GitHub)</strong> 
Git으로 내 코드의 모든 변경사항을 기록하고, GitHub에 올려두면 내 컴퓨터가 고장 나도 코드는 안전하다.</li>
<li><strong>체계적인 개인 프로젝트 관리 (Git) + 훌륭한 공개 포트폴리오 (GitHub)</strong> 
Git으로 내 프로젝트를 관리하고 GitHub에 꾸준히 올리는 것은, &quot;나는 이렇게 코딩하고 성장해왔다&quot;를 보여주는 가장 확실한 이력서가 된다.</li>
<li><strong>강력한 독립 작업 (Git Branch) + 체계적인 팀 협업 (GitHub Pull Request)</strong> 
Git의 브랜치 기능으로 내 작업을 독립적으로 수행하고, GitHub의 Pull Request 기능으로 팀원들에게 코드 리뷰를 요청하며 안전하게 코드를 병합할 수 있다.</li>
</ol>
<hr>
<h2 id="기초-사용법">기초 사용법</h2>
<h3 id="1-최초-설정-컴퓨터에-딱-한-번만">1. 최초 설정 (컴퓨터에 딱 한 번만)</h3>
<p>Git을 설치한 후, 커밋에 기록될 내 정보를 설정한다.</p>
<p>Git Bash를 설치하고 다음을 입력한다. </p>
<p>이때 복사와 붙여넣기는 <code>ctrl + c</code> <code>ctrl + v</code> 대신 <code>ctrl + insert</code> <code>shift + insert</code>로 사용한다.</p>
<pre><code class="language-bash">git config --global user.name &quot;Your Name&quot;
git config --global user.email &quot;your.email@example.com&quot;</code></pre>
<h3 id="2-로컬-프로젝트-생성-및-github에-올리기">2. 로컬 프로젝트 생성 및 GitHub에 올리기</h3>
<p><strong>1단계: 내 컴퓨터에서 Git 관리 시작</strong></p>
<pre><code class="language-bash"># 프로젝트 폴더를 만들고 이동
mkdir my-project
cd my-project

# 이 폴더를 Git으로 관리 시작!
git init</code></pre>
<p>아니면 vscode에서 터미널 창에서 bash를 선택하거나 프로젝트 폴터에서 우클릭 후 이 경로에서 bash 실행하기를 통해 생략할 수 있다.</p>
<p><strong>2단계: 코드 수정 및 로컬에 버전 저장 (add &amp; commit)</strong></p>
<pre><code class="language-bash"># 변경된 모든 파일을 스테이징(장바구니에 담기)
git add .

# 장바구니에 담긴 내용을 하나의 버전으로 저장(커밋)
git commit -m &quot;Initial commit: Add README.md&quot;</code></pre>
<p><strong>3단계: GitHub 원격 저장소 생성 및 연결</strong></p>
<ol>
<li><p><strong>GitHub</strong>에 로그인하여 <code>New repository</code> 버튼으로 새로운 저장소를 만든다. </p>
</li>
<li><p>생성된 저장소의 URL(예: <code>https://github.com/YourName/my-project.git</code>)을 복사한다.</p>
</li>
<li><p>내 컴퓨터 터미널에서 아래 명령어로 로컬 저장소와 원격 저장소를 연결한다.</p>
</li>
</ol>
<pre><code class="language-bash"># 원격 저장소 주소를 &#39;origin&#39;이라는 이름으로 추가
git remote add origin https://github.com/YourName/my-project.git

# 로컬의 main 브랜치를 원격 저장소(origin)로 업로드(push)
git push -u origin main</code></pre>
<p>이제 <code>my-project</code>는 내 컴퓨터와 GitHub 양쪽에 모두 존재하며 서로 연결되었다.</p>
<h3 id="3-협업의-기본-사이클-pull-→-branch-→-commit-→-push-→-pull-request">3. 협업의 기본 사이클: Pull → Branch → Commit → Push → Pull Request</h3>
<p>팀 프로젝트에 참여할 때의 일반적인 작업 흐름이다.</p>
<p><strong>1단계: 작업 시작 전, 항상 최신 코드로 동기화</strong></p>
<p>다른 팀원이 수정했을지 모르는 최신 코드를 원격 저장소에서 내 컴퓨터로 가져온다.</p>
<pre><code class="language-bash">git pull origin main</code></pre>
<p><strong>2단계: 나만의 작업 공간(브랜치) 만들기</strong></p>
<p><code>main</code> 브랜치를 직접 건드리지 않고, 새로운 기능을 개발하기 위한 독립된 브랜치를 만든다.</p>
<pre><code class="language-bash"># &#39;feature/login&#39; 이라는 브랜치를 만들고 그곳으로 이동
git checkout -b feature/login</code></pre>
<p><strong>3단계: 기능 개발 및 커밋</strong></p>
<p>이제 <code>feature/login</code> 브랜치에서 마음껏 코드를 수정하고, <code>add</code>와 <code>commit</code>으로 작업 단위를 저장한다.</p>
<pre><code class="language-bash"># (열심히 코딩...)
git add .
git commit -m &quot;Feat: Implement user login logic&quot;</code></pre>
<p><strong>4단계: 내 브랜치를 원격 저장소에 업로드</strong></p>
<p>내가 만든 <code>feature/login</code> 브랜치를 팀원들이 볼 수 있도록 GitHub에 올린다.</p>
<pre><code class="language-bash">git push origin feature/login</code></pre>
<p><strong>5단계: Pull Request(PR) 생성</strong></p>
<p><strong>가장 중요한 협업 과정이다.</strong></p>
<ol>
<li><strong>GitHub</strong> 사이트로 이동하면 &quot;<code>feature/login</code> 브랜치에 변경사항이 있으니 Pull Request를 생성할래요?&quot; 라는 초록색 버튼이 보인다.</li>
<li>버튼을 눌러 내가 작업한 내용과 리뷰를 요청하는 메시지를 작성하고 PR을 생성한다.</li>
<li>이제 팀원들은 내 코드를 보고 의견을 남길 수 있다. 리뷰가 끝나고 승인이 나면, 관리자가 <code>Merge</code> 버튼을 눌러 내 코드를 <code>main</code> 브랜치에 안전하게 합친다.</li>
</ol>
<hr>
<p>Git과 GitHub는 더 이상 선택이 아닌 <strong>필수</strong>라고 한다.</p>
<p>협업할때 뿐만 아니라 개인 프로젝트를 할때도 버전을 효율적으로 관리하고 포트폴리오로도 활용하기 위해 Git이 중요한 것 같다.</p>
<p>이전에 진행하였던 나의 프로젝트도 GitHub에 업로드해서 수정사항들을 더 체계적으로 관리하고 새로운 업데이트가 생겼을때 bash를 이용해 더욱 편하게 관리할 수 있을 것이다.</p>
<p>앞으론 터미널의 claude code와 Mcp를 활용해보며 교수님이 학기중에 수도 없이 말씀하신 바이브 코딩 능력을 길러봐야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DataBase] 무결성 제약조건(Integrity Constraint)]]></title>
            <link>https://velog.io/@kt_gml/DataBase-%EB%AC%B4%EA%B2%B0%EC%84%B1-%EC%A0%9C%EC%95%BD%EC%A1%B0%EA%B1%B4-Integrity-Constraint</link>
            <guid>https://velog.io/@kt_gml/DataBase-%EB%AC%B4%EA%B2%B0%EC%84%B1-%EC%A0%9C%EC%95%BD%EC%A1%B0%EA%B1%B4-Integrity-Constraint</guid>
            <pubDate>Fri, 04 Jul 2025 08:14:30 GMT</pubDate>
            <description><![CDATA[<h2 id="데이터-무결성이란-무엇인가">데이터 무결성이란 무엇인가?</h2>
<p><strong>데이터 무결성</strong>은 데이터베이스 내의 데이터에 대해 <strong>정확성(Accuracy), 일관성(Consistency), 유효성(Validity)</strong>을 유지하는 것을 의미한다. </p>
<p>즉, 데이터베이스에 저장된 값은 항상 의도된 형식과 규칙에 맞게 저장되어야 하며, 데이터 간의 관계 또한 모순이 없어야 한다.</p>
<p><strong>데이터베이스 시스템(DBMS)</strong>은 이러한 무결성을 강제하기 위해 다양한 <strong>제약조건(Constraint)</strong> 기능을 제공한다.</p>
<hr>
<h2 id="데이터-무결성을-지키는-4가지-제약조건">데이터 무결성을 지키는 4가지 제약조건</h2>
<h3 id="1-개체-무결성-entity-integrity">1. 개체 무결성 (Entity Integrity)</h3>
<p>기본 키(Primary Key)는 절대 NULL 값을 가질 수 없다.</p>
<p><strong>개체 무결성</strong>은 테이블 내의 모든 행(Row)이 고유하게 식별될 수 있도록 보장하는 규칙이다. </p>
<p>이를 위해 사용되는 것이 바로 <strong>기본 키(Primary Key)</strong>이다.</p>
<h3 id="2-기본키-무결성-primary-key-integrity">2. 기본키 무결성 (Primary Key Integrity)</h3>
<p>기본 키의 값은 중복될 수 없다. </p>
<p>만약 학생 테이블의 &#39;학생 ID&#39;가 없거나(NULL) 여러 학생이 동일한 ID를 가진다면 특정 학생을 정확하게 찾아내거나 구분할 수 없다.</p>
<p><strong>PRIMARY KEY 활용</strong></p>
<pre><code class="language-sql">CREATE TABLE Student (
    StudentID INT PRIMARY KEY, -- StudentID는 NULL이거나 중복될 수 없다.
    StudentName VARCHAR(50) NOT NULL,
    Major VARCHAR(50)
);</code></pre>
<h3 id="3-참조-무결성-referential-integrity">3. 참조 무결성 (Referential Integrity)</h3>
<p>외래 키(Foreign Key)는 부모 테이블의 기본 키에 존재하는 값이거나 NULL이어야 한다.</p>
<p><strong>참조 무결성</strong>은 두 개의 테이블이 관계를 맺고 있을 때, 그 관계가 항상 유효하도록 보장하는 규칙이다. 이는 <strong>외래 키(Foreign Key)</strong>를 통해 지켜진다.</p>
<p>&#39;수강&#39; 테이블에 존재하지도 않는 &#39;학생 ID&#39;나 개설되지도 않은 &#39;과목 코드&#39;가 입력된다면 데이터는 의미를 잃는다. </p>
<p>참조 무결성은 이처럼 &quot;없는 학생이 수강 신청을 하는&quot; 등의 논리적 오류를 막아준다.</p>
<p><strong>자식 테이블(수강)의 컬럼에 <code>FOREIGN KEY</code> 제약조건을 설정</strong></p>
<pre><code class="language-sql">-- 부모 테이블
CREATE TABLE Student (
    StudentID INT PRIMARY KEY,
    StudentName VARCHAR(50)
);

-- 자식 테이블
CREATE TABLE Enrollment (
    EnrollmentID INT PRIMARY KEY,
    StudentID INT, -- 이 컬럼이 외래 키가 된다.
    CourseCode VARCHAR(10),
    FOREIGN KEY (StudentID) REFERENCES Student(StudentID) -- Student 테이블의 StudentID를 참조한다.
);</code></pre>
<p>이렇게 설정하면, <code>Student</code> 테이블에 존재하지 않는 <code>StudentID</code>는 <code>Enrollment</code> 테이블에 삽입될 수 없다.</p>
<h3 id="4-도메인-무결성-domain-integrity">4. 도메인 무결성 (Domain Integrity)</h3>
<p>컬럼에 저장되는 데이터는 정해진 규칙(타입, 제약조건)을 반드시 따라야 한다.</p>
<p><strong>도메인 무결성</strong>은 테이블의 특정 컬럼에 입력될 데이터의 &#39;범위(Domain)&#39;를 사용자가 정한 규칙에 맞도록 강제하는 것이다. </p>
<p>&#39;성별&#39; 컬럼에 &#39;남&#39;, &#39;여&#39; 외에 &#39;99999&#39; 같은 엉뚱한 값이 들어가거나, &#39;나이&#39; 컬럼에 음수 값이 들어가는 것을 막아야 데이터의 유효성이 보장된다.</p>
<p>   <strong>데이터 타입(Data Type):</strong> <code>INT</code>, <code>VARCHAR(10)</code>, <code>DATE</code> 등으로 타입을 지정한다.</p>
<p>   <strong>NOT NULL:</strong> NULL 값 입력을 허용하지 않는다.</p>
<p>   <strong>UNIQUE:</strong> 중복된 값을 허용하지 않는다.</p>
<p>   <strong>CHECK:</strong> 특정 조건에 맞는 값만 허용한다. (예: 나이는 0보다 커야 한다)</p>
<p>   <strong>DEFAULT:</strong> 값이 입력되지 않았을 때 기본으로 설정될 값을 지정한다.</p>
<pre><code class="language-sql">CREATE TABLE Member (
    MemberID INT PRIMARY KEY,
    Gender CHAR(1) NOT NULL CHECK (Gender IN (&#39;M&#39;, &#39;F&#39;)), -- 성별은 &#39;M&#39; 또는 &#39;F&#39;만 가능
    Age INT CHECK (Age &gt; 0), -- 나이는 양수만 가능
    Email VARCHAR(100) UNIQUE, -- 이메일은 중복될 수 없다.
    JoinDate DATE DEFAULT NOW() -- 가입일은 기본으로 현재 날짜가 입력된다.
);</code></pre>
<hr>
<h3 id="정규화와-무결성의-관계">정규화와 무결성의 관계</h3>
<p><strong>정규화(Normalization)</strong> 
데이터의 중복을 제거하고 테이블 구조를 효율적으로 만드는 <strong>설계 과정</strong>이다. 정규화를 잘하면 데이터 이상 현상이 줄어들어 무결성을 지키기 쉬워진다. </p>
<p><strong>즉, 무결성을 위한 기반 공사이다.</strong></p>
<p><strong>무결성(Integrity)</strong> 
설계된 구조 위에서 데이터의 정확성과 일관성을 보장하는 <strong>규칙</strong>이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DataBase] 정규화(Normalization)]]></title>
            <link>https://velog.io/@kt_gml/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%9D%98-%EC%A0%95%EA%B7%9C%ED%99%94Normalization</link>
            <guid>https://velog.io/@kt_gml/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%9D%98-%EC%A0%95%EA%B7%9C%ED%99%94Normalization</guid>
            <pubDate>Fri, 04 Jul 2025 06:10:11 GMT</pubDate>
            <description><![CDATA[<h2 id="정규화normalization란-">정규화(Normalization)란 ?</h2>
<p><strong>데이터베이스 정규화</strong>는 관계형 데이터베이스에서 <strong>데이터의 중복을 최소화하고, 데이터의 일관성과 무결성을 확보하기 위해 테이블을 구조화하는 과정</strong>이다. </p>
<p>잘못 설계된 테이블에서는 데이터를 추가, 수정, 삭제할 때 예기치 않은 문제가 발생할 수 있다. </p>
<p>정규화는 이러한 <strong>&#39;이상 현상&#39;</strong>을 방지하는 핵심적인 역할을 한다.</p>
<hr>
<h2 id="정규화는-왜-필요한가-데이터-이상-현상">정규화는 왜 필요한가? (데이터 이상 현상)</h2>
<p>만약 <strong>정규화</strong>를 제대로 하지 않으면 다음과 같은 <strong>데이터 이상 현상</strong>이 발생할 수 있다.</p>
<p>아래와 같은 <strong>&#39;수강 신청 테이블&#39;</strong>이 있다고 가정해보자.</p>
<table>
<thead>
<tr>
<th align="center">학생 ID</th>
<th align="center">학생 이름</th>
<th align="center">학과</th>
<th align="center">과목 코드</th>
<th align="center">과목명</th>
<th align="center">담당 교수</th>
</tr>
</thead>
<tbody><tr>
<td align="center">101</td>
<td align="center">김철수</td>
<td align="center">컴퓨터공학</td>
<td align="center">CS101</td>
<td align="center">컴퓨터 개론</td>
<td align="center">이영희</td>
</tr>
<tr>
<td align="center">102</td>
<td align="center">박민지</td>
<td align="center">전자공학</td>
<td align="center">CS101</td>
<td align="center">컴퓨터 개론</td>
<td align="center">이영희</td>
</tr>
<tr>
<td align="center">101</td>
<td align="center">김철수</td>
<td align="center">컴퓨터공학</td>
<td align="center">EE201</td>
<td align="center">회로 이론</td>
<td align="center">김민준</td>
</tr>
</tbody></table>
<ol>
<li><p><strong>삽입 이상 (Insertion Anomaly)</strong>
아직 수강 신청을 하지 않은 새로운 학생 &#39;홍길동&#39;을 추가하고 싶어도, 신청한 &#39;과목 코드&#39;가 없으면 데이터를 추가할 수 없다. </p>
<p>즉, 불필요한 데이터(과목 정보)가 있어야만 학생 정보를 추가할 수 있는 모순이 발생한다.</p>
</li>
<li><p><strong>갱신 이상 (Update Anomaly)</strong>
&#39;이영희&#39; 교수가 &#39;최영희&#39;로 개명했다면, &#39;컴퓨터 개론&#39;을 수강하는 모든 학생의 데이터에서 교수 이름을 일일이 바꿔야 한다. </p>
<p>만약 하나라도 빠뜨리면 데이터의 일관성이 깨진다.</p>
</li>
<li><p><strong>삭제 이상 (Deletion Anomaly)</strong>
&#39;박민지&#39; 학생이 &#39;컴퓨터 개론&#39; 수강을 취소하면 해당 행이 삭제된다. </p>
<p>이때 &#39;컴퓨터 개론&#39; 과목을 수강하는 학생이 아무도 없다면, &#39;컴퓨터 개론&#39; 과목 정보 자체가 데이터베이스에서 사라져 버린다.</p>
</li>
</ol>
<p>정규화는 바로 이러한 문제들을 해결하기 위해 테이블을 논리적으로 분해하는 과정이다.</p>
<hr>
<h2 id="정규화-과정-1nf-2nf-3nf">정규화 과정 (1NF, 2NF, 3NF)</h2>
<h3 id="제1정규화-1nf-first-normal-form">제1정규화 (1NF, First Normal Form)</h3>
<blockquote>
<p><strong>규칙: 테이블의 모든 컬럼은 반드시 원자값을 가져야 한다.</strong></p>
</blockquote>
<p>원자값이라는 것은 컬럼의 값이 더 이상 쪼개질 수 없다는 의미이다.</p>
<p><strong>[정규화 전]</strong></p>
<table>
<thead>
<tr>
<th align="center">학생 ID</th>
<th align="center">학생 이름</th>
<th align="center">수강 과목</th>
</tr>
</thead>
<tbody><tr>
<td align="center">101</td>
<td align="center">김철수</td>
<td align="center">컴퓨터 개론, 자료구조</td>
</tr>
<tr>
<td align="center">102</td>
<td align="center">박민지</td>
<td align="center">회로 이론</td>
</tr>
</tbody></table>
<p>위 테이블에서 &#39;수강 과목&#39; 컬럼은 두 개의 값을 가지고 있으므로 제1정규형을 위반한다.</p>
<p><strong>[제1정규화 후]</strong></p>
<table>
<thead>
<tr>
<th align="center">학생 ID</th>
<th align="center">학생 이름</th>
<th align="center">수강 과목</th>
</tr>
</thead>
<tbody><tr>
<td align="center">101</td>
<td align="center">김철수</td>
<td align="center">컴퓨터 개론</td>
</tr>
<tr>
<td align="center">101</td>
<td align="center">김철수</td>
<td align="center">자료구조</td>
</tr>
<tr>
<td align="center">102</td>
<td align="center">박민지</td>
<td align="center">회로 이론</td>
</tr>
</tbody></table>
<p>이렇게 각 행이 하나의 의미를 갖도록 값을 분리하면 제1정규화가 완료된다.</p>
<h3 id="제2정규화-2nf-second-normal-form">제2정규화 (2NF, Second Normal Form)</h3>
<blockquote>
<p><strong>규칙: 제1정규화를 만족해야 하고, 부분 함수 종속성(Partial Functional Dependency)을 제거해야 한다.</strong></p>
</blockquote>
<p>쉽게 말해, <strong>기본 키(Primary Key)가 여러 컬럼으로 구성된 복합 키일 경우, 테이블의 다른 모든 컬럼은 기본 키 전체에 종속되어야 한다.</strong> 기본 키의 일부에만 종속된 컬럼이 있다면 테이블을 분리해야 한다.</p>
<p><strong>[정규화 전]</strong></p>
<p>기본 키가 <code>(학생 ID, 과목 코드)</code>인 테이블이 있다.</p>
<table>
<thead>
<tr>
<th align="center">학생 ID</th>
<th align="center">과목 코드</th>
<th align="center">학생 이름</th>
<th align="center">학과</th>
<th align="center">성적</th>
</tr>
</thead>
<tbody><tr>
<td align="center">101</td>
<td align="center">CS101</td>
<td align="center">김철수</td>
<td align="center">컴퓨터공학</td>
<td align="center">A+</td>
</tr>
<tr>
<td align="center">101</td>
<td align="center">EE201</td>
<td align="center">김철수</td>
<td align="center">컴퓨터공학</td>
<td align="center">B</td>
</tr>
<tr>
<td align="center">102</td>
<td align="center">CS101</td>
<td align="center">박민지</td>
<td align="center">전자공학</td>
<td align="center">A</td>
</tr>
</tbody></table>
<p><code>학생 이름</code>, <code>학과</code>는 기본 키의 일부인 <code>학생 ID</code>에만 종속된다. (<code>학생 ID</code>만 알면 <code>학생 이름</code>을 알 수 있다)</p>
<p><code>성적</code>은 <code>학생 ID</code>와 <code>과목 코드</code> 모두에 종속된다. (누가 어떤 과목을 들었는지 알아야 성적을 알 수 있다)</p>
<p>이처럼 기본 키의 일부에만 종속되는 &#39;부분 함수 종속성&#39;이 존재하므로 제2정규형을 위반한다.</p>
<p><strong>[제2정규화 후]</strong></p>
<p>부분 함수 종속성을 제거하기 위해 테이블을 다음과 같이 분리한다.</p>
<p><strong>학생 테이블</strong></p>
<table>
<thead>
<tr>
<th align="center">학생 ID</th>
<th align="center">학생 이름</th>
<th align="center">학과</th>
</tr>
</thead>
<tbody><tr>
<td align="center">101</td>
<td align="center">김철수</td>
<td align="center">컴퓨터공학</td>
</tr>
<tr>
<td align="center">102</td>
<td align="center">박민지</td>
<td align="center">전자공학</td>
</tr>
</tbody></table>
<p><strong>수강 테이블</strong></p>
<table>
<thead>
<tr>
<th align="center">학생 ID</th>
<th align="center">과목 코드</th>
<th align="center">성적</th>
</tr>
</thead>
<tbody><tr>
<td align="center">101</td>
<td align="center">CS101</td>
<td align="center">A+</td>
</tr>
<tr>
<td align="center">101</td>
<td align="center">EE201</td>
<td align="center">B</td>
</tr>
<tr>
<td align="center">102</td>
<td align="center">CS101</td>
<td align="center">A</td>
</tr>
</tbody></table>
<p>이제 모든 컬럼이 각 테이블의 기본 키 전체에 종속되므로 <strong>제2정규화</strong>가 완료된다.</p>
<h3 id="제3정규화-3nf-third-normal-form">제3정규화 (3NF, Third Normal Form)</h3>
<blockquote>
<p><strong>규칙: 제2정규화를 만족해야 하고, 이행적 함수 종속성(Transitive Functional Dependency)을 제거해야 한다.</strong></p>
</blockquote>
<p><strong>이행적 함수 종속성</strong>이란, <code>A → B</code>이고 <code>B → C</code>일 때 <code>A → C</code>가 성립하는 관계를 말한다. </p>
<p>즉, <strong>기본 키가 아닌 일반 컬럼이 다른 일반 컬럼을 결정해서는 안 된다.</strong></p>
<p><strong>[정규화 전]</strong></p>
<table>
<thead>
<tr>
<th align="center">과목 코드</th>
<th align="center">과목명</th>
<th align="center">담당 교수</th>
<th align="center">교수 연락처</th>
</tr>
</thead>
<tbody><tr>
<td align="center">CS101</td>
<td align="center">컴퓨터 개론</td>
<td align="center">이영희</td>
<td align="center">010-1111-1111</td>
</tr>
<tr>
<td align="center">DS202</td>
<td align="center">자료구조</td>
<td align="center">김민준</td>
<td align="center">010-2222-2222</td>
</tr>
<tr>
<td align="center">OS303</td>
<td align="center">운영체제</td>
<td align="center">이영희</td>
<td align="center">010-1111-1111</td>
</tr>
</tbody></table>
<p>이 테이블에서 <code>과목 코드</code>는 기본 키이다.</p>
<p><code>과목 코드</code> → <code>담당 교수</code> (과목 코드를 알면 담당 교수를 알 수 있다)</p>
<p><code>담당 교수</code> → <code>교수 연락처</code> (담당 교수를 알면 그의 연락처를 알 수 있다)</p>
<p>기본 키가 아닌 &#39;담당 교수&#39; 컬럼이 &#39;교수 연락처&#39; 컬럼을 결정하고 있으므로 이행적 종속 관계가 존재한다. 이는 제3정규형을 위반한다. (교수 연락처가 바뀌면 여러 행을 수정해야 하는 갱신 이상 발생)</p>
<p><strong>[제3정규화 후]</strong></p>
<p>이행적 종속성을 제거하기 위해 테이블을 분리한다.</p>
<p><strong>과목 테이블</strong></p>
<table>
<thead>
<tr>
<th align="center">과목 코드</th>
<th align="center">과목명</th>
<th align="center">담당 교수</th>
</tr>
</thead>
<tbody><tr>
<td align="center">CS101</td>
<td align="center">컴퓨터 개론</td>
<td align="center">이영희</td>
</tr>
<tr>
<td align="center">DS202</td>
<td align="center">자료구조</td>
<td align="center">김민준</td>
</tr>
<tr>
<td align="center">OS303</td>
<td align="center">운영체제</td>
<td align="center">이영희</td>
</tr>
</tbody></table>
<p><strong>교수 테이블</strong></p>
<table>
<thead>
<tr>
<th align="center">담당 교수</th>
<th align="center">교수 연락처</th>
</tr>
</thead>
<tbody><tr>
<td align="center">이영희</td>
<td align="center">010-1111-1111</td>
</tr>
<tr>
<td align="center">김민준</td>
<td align="center">010-2222-2222</td>
</tr>
</tbody></table>
<p>이렇게 분리하면 더 이상 일반 컬럼끼리 종속되는 관계가 사라지므로 제3정규화가 완료된다.</p>
<h2 id="결론">결론</h2>
<ul>
<li><strong>1NF:</strong> 값은 원자적이어야 한다.</li>
<li><strong>2NF:</strong> 부분 종속성을 제거해야 한다. (복합 키일 때)</li>
<li><strong>3NF:</strong> 이행 종속성을 제거해야 한다. (일반 컬럼끼리의 종속)</li>
</ul>
<p>실무에서는 보통 제3정규화까지 마치는 것을 목표로 한다. </p>
<p>더 높은 단계의 정규화도 있지만, 3NF까지 진행하면 대부분의 데이터 이상 현상을 해결할 수 있다.</p>
<p>정규화는 데이터의 중복을 막고 무결성을 지키기 위한 필수적인 과정이다. </p>
<p>정규화된 데이터베이스는 장기적으로 훨씬 안정적이고 확장성 있는 시스템의 기반이 된다.</p>
<p>강의에서 교수님께서 항상 하시던 말씀이 있다. </p>
<p>정규화를 배우는 이유는 데이터베이스를 정규화 하기 위함이라기 보다는 설계시에 정규화가 필요없는 좋은 데이터베이스를 설계하기 위함이라고 하셨다.</p>
<p>정규화를 이해하고 애초부터 정규화가 필요없는 데이터베이스를 설계해야겠다.</p>
<p>다음 글에서는 무결성에 대해 다뤄보겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C] 형변환(Type Casting)과 연산자 우선순위 (14626 - ISBN)]]></title>
            <link>https://velog.io/@kt_gml/14626-ISBN</link>
            <guid>https://velog.io/@kt_gml/14626-ISBN</guid>
            <pubDate>Wed, 02 Jul 2025 08:13:52 GMT</pubDate>
            <description><![CDATA[<h2 id="14626---isbn">14626 - ISBN</h2>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/ed4c3d15-a122-4ed5-81ec-218a3d8e82de/image.png" alt=""></p>
<blockquote>
<p>*<em>BAEKJOON *</em>: <a href="https://www.acmicpc.net/problem/14626">14626 - ISBN</a></p>
</blockquote>
<hr>
<pre><code class="language-c">#include &lt;stdio.h&gt;

int main() {
  char check;
  int num[13] = {0,}, sum = 0, flag, result;

  for (int i = 0; i &lt; 13; i++) {
    scanf(&quot;%c&quot;, &amp;check);
    if (check == &#39;*&#39;)
      flag = i;
    else
      num[i] = check-48;
  }
  for (int i = 0; i &lt; 13; i++) {
    if(i!=flag){
      if (i % 2 == 1)
        sum += num[i] * 3;
      else
        sum += num[i];
    } 
  }
  if (flag % 2 == 0) { // 1을 곱하는 경우
    for(int i=0; i&lt;10; i++){ 
      if((sum+i)%10==0){
        printf(&quot;%d&quot;, i);
      }
    }
  } else { // 3을 곱하는 경우
    for(int i=0; i&lt;10; i++){ 
      if((sum+3*i)%10==0){
        printf(&quot;%d&quot;, i);
      }
    }
  }
  return 0;
}</code></pre>
<p>오랜만에 알고리즘 문제를 풀어봤더니 내가 생각한대로 동작을 안했다.</p>
<p>디버깅 과정에서 배운 내용을 복습겸 정리해보려고 한다.</p>
<hr>
<h2 id="형변환type-casting">형변환(Type Casting)</h2>
<p>형변환은 <strong>데이터의 자료형을 다른 자료형으로 바꾸는 것</strong>이다.</p>
<p>예를 들어 정수 <code>int</code>를 문자 <code>char</code>로 바꾸는 식이다.</p>
<h3 id="1-자동-형변환-implicit-type-conversion">1. 자동 형변환 (Implicit Type Conversion)</h3>
<p>컴파일러가 &quot;알아서&quot; 해주는 형변환을 자동 형변환이라고 한다.</p>
<p>주로 <strong>데이터 손실이 없는 방향</strong>으로, 즉 더 작은 자료형이 더 큰 자료형으로 바뀔 때 일어난다.</p>
<p><strong>규칙</strong>
<code>char</code> → <code>int</code> → <code>long</code> → <code>float</code> → <code>double</code>
    (더 표현 범위가 넓고 정밀한 쪽으로 자동 변환)</p>
<p><strong>언제 일어나는가?</strong>
    서로 다른 자료형을 가진 변수끼리 연산할 때 발생한다.</p>
<pre><code class="language-c">    #include &lt;stdio.h&gt;

    int main() {
        int a = 10;
        double b = 3.0;

        // a(int)가 b(double)와 계산되기 위해 
        //컴파일러에 의해 임시로 double형으로 자동 형변환된다
        // 10 -&gt; 10.0 으로 바뀐 뒤에 3.0과 더해진다
        printf(&quot;결과: %f\n&quot;, a + b); // 결과: 13.000000

        return 0;
    }</code></pre>
<p>이때 큰 자료형을 작은 자료형에 대입하면 <strong>데이터 손실</strong>이 발생할 수 있다. </p>
<pre><code class="language-c">    int c = 3.141592; // double형 실수를 int형에 넣으려고 함
    printf(&quot;%d\n&quot;, c); // 소수점 이하가 모두 잘리고 &#39;3&#39;만 저장된다
</code></pre>
<h3 id="2-명시적-형변환-explicit-type-casting">2. 명시적 형변환 (Explicit Type Casting)</h3>
<p>개발자가 <strong>강제로</strong> 자료형을 바꾸는 것이다. </p>
<p>데이터 손실을 감수하고서라도 형변환이 필요할 때 사용한다.</p>
<p><strong>사용법</strong>
<code>(바꿀_자료형)변수_또는_값</code></p>
<p><strong>언제 사용하는가?</strong>
    가장 대표적인 예시는 <strong>정수 나눗셈의 결과를 실수로 얻고 싶을 때 사용한다.</strong></p>
<pre><code class="language-c">
    #include &lt;stdio.h&gt;

    int main() {
        int a = 10;
        int b = 3;

        // (1) 잘못된 경우
        // 정수와 정수의 나눗셈 결과는 무조건 정수(몫)이다.
        printf(&quot;잘못된 계산: %f\n&quot;, a / b); // 3.000000 같은 이상한 값이 나옴

        // (2) 올바른 경우 (명시적 형변환 사용)
        // 변수 a를 연산 순간에만 잠시 double형으로 강제 변환한다.
        // (double)a / b  -&gt;  10.0 / 3  -&gt;  double과 int의 연산이 됨
        // b는 자동으로 double형으로 &#39;자동 형변환&#39;되어 계산된다.
        printf(&quot;올바른 계산: %f\n&quot;, (double)a / b); // 결과: 3.333333

        return 0;
    }
</code></pre>
<h2 id="int형과-char형-변수-사이의-형변환">Int형과 Char형 변수 사이의 형변환</h2>
<p>위의 형변환에 대해서는 잘 알고 있었지만 Int와 Char 사이의 형변환을 잘 이해하지 못해서 문제를 틀렸다.</p>
<h3 id="int5-53">(int)&#39;5&#39;= 53</h3>
<p><strong>(int)char</strong> 형변환이 잘 안되길래 더 공부해봤다.</p>
<p>알고보니 형변환은 아주 잘 되고 있는 게 맞지만, 우리가 기대하는 방식이 아니었다.</p>
<h3 id="왜-int5는-5가-아닐까-">왜 (int)&#39;5&#39;는 5가 아닐까 ?</h3>
<p>컴퓨터는 &#39;문자&#39;를 그대로 저장하지 못하기 때문에 숫자를 사용한다.</p>
<p>이때 어떤 숫자가 어떤 문자를 뜻할지 약속한것이 *<em>아스키(ASCII) *</em>코드이다.</p>
<p>C언어에서 char 자료형은 사실 문자가 아니라 &#39;문자에 해당하는 아스키 코드&#39;를 저장하는 8비트 정수형이다.</p>
<p>우리가 char c = &#39;5&#39;; 라고 쓰면,</p>
<p>컴퓨터는 변수 c에 <strong>&#39;문자 5&#39;에 해당하는 아스키 코드 값인 53</strong>을 저장한다.</p>
<p>&#39;0&#39;    48
&#39;1&#39;    49
&#39;2&#39;    50
&#39;3&#39;    51
&#39;4&#39;    52
&#39;5&#39;    53
...    ...
&#39;9&#39;    57</p>
<p>따라서 (int)c 라고 형변환을 하면, c에 저장된 값 <strong>53</strong>을 그냥 더 큰 정수 자료형인 int로 옮겨 담는 것이다. </p>
<p>이러한 원리 때문에 값이 바뀌지 않고 53이 그대로 유지되는 것이다.</p>
<h3 id="올바른-형변환-방법">올바른 형변환 방법</h3>
<h4 id="1-한-글자-숫자0--9를-바꿀-때-아스키-코드-값-빼기">1. 한 글자 숫자(&#39;0&#39; ~ &#39;9&#39;)를 바꿀 때: 아스키 코드 값 빼기</h4>
<p>가장 기본적이고 빠른 방법으로 위의 알고리즘 문제에서도 사용되었다.</p>
<pre><code>&#39;5&#39; - &#39;0&#39; → 53 - 48 → 5

&#39;8&#39; - &#39;0&#39; → 56 - 48 → 8</code></pre><pre><code class="language-c">#include &lt;stdio.h&gt;

int main() {
    char c1 = &#39;7&#39;;

    // 방법 1: &#39;0&#39; 문자를 빼기 (가장 추천하는 방식, 직관적이다)
    int num1 = c1 - &#39;0&#39;;

    // 방법 2: 48 숫자를 빼기 (결과는 같지만, 48이 뭔지 모르면 헷갈림)
    int num2 = c1 - 48;

    printf(&quot;문자 &#39;7&#39;은 숫자 %d 입니다.\n&quot;, num1); // 결과: 문자 &#39;7&#39;은 숫자 7 입니다.
    printf(&quot;문자 &#39;7&#39;은 숫자 %d 입니다.\n&quot;, num2); // 결과: 문자 &#39;7&#39;은 숫자 7 입니다.

    return 0;
}</code></pre>
<h4 id="2-문자열123을-숫자로-바꿀-때-atoi-함수">2. 문자열(&quot;123&quot;)을 숫자로 바꿀 때: atoi() 함수</h4>
<p>여러 글자로 이루어진 숫자 문자열을 한 번에 정수로 바꿀 때 사용한다. </p>
<p><strong>atoi</strong>는 <code>ASCII to Integer</code> 의 줄임말이다.</p>
<p><code>stdlib.h</code> 헤더 파일에 포함된 함수로, 숫자 문자열의 시작 주소를 받아서 정수(int)로 변환해 돌려준다.</p>
<pre><code class="language-c">#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt; // atoi 함수를 쓰려면 꼭 포함해야 함!

int main() {
    char str[] = &quot;14626&quot;; // 큰따옴표로 묶인 문자열 (내부적으로는 char 배열)

    int num = atoi(str);

    printf(&quot;문자열 \&quot;%s\&quot;는 숫자 %d 입니다.\n&quot;, str, num); // 결과: 문자열 &quot;14626&quot;는 숫자 14626 입니다.
    printf(&quot;계산도 가능: %d * 2 = %d\n&quot;, num, num * 2); // 결과: 계산도 가능: 14626 * 2 = 29252

    return 0;
}</code></pre>
<p>atoi는 <code>char c = &#39;5&#39;</code> 같은 단일 문자 변수에는 직접 쓸 수 없다. </p>
<p>항상 char 배열, 즉 문자열을 인자로 넣어줘야 한다.</p>
<hr>
<h2 id="c언어-연산자-우선순위-operator-precedence">C언어 연산자 우선순위 (Operator Precedence)</h2>
<p><strong>연산자 우선순위</strong>를 기존에도 알고 있었지만 연산이 많아지니까 헷갈려서 정리해보겠다.</p>
<p>C언어도 연산자마다 정해진 <strong>계산 순서</strong>가 있다.</p>
<p>아래 표는 <strong>위에서 아래로 갈수록 우선순위가 낮아진다.</strong> </p>
<p>같은 줄에 있는 연산자들은 우선순위가 같다.</p>
<table>
<thead>
<tr>
<th align="center">우선순위</th>
<th align="left">연산자 종류</th>
<th align="left">연산자</th>
<th align="center">결합 방향</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><strong>1 (최고)</strong></td>
<td align="left">후위(postfix), 구조체</td>
<td align="left"><code>()</code> <code>[]</code> <code>.</code> <code>-&gt;</code> <code>++</code> <code>--</code></td>
<td align="center">L → R</td>
</tr>
<tr>
<td align="center"><strong>2</strong></td>
<td align="left">단항(unary)</td>
<td align="left"><code>++</code> <code>--</code> <code>+</code> <code>-</code> <code>!</code> <code>~</code> <code>*</code> <code>&amp;</code> <code>(type)</code> <code>sizeof</code></td>
<td align="center">R → L</td>
</tr>
<tr>
<td align="center"><strong>3</strong></td>
<td align="left">곱셈/나눗셈</td>
<td align="left"><code>*</code> <code>/</code> <code>%</code></td>
<td align="center">L → R</td>
</tr>
<tr>
<td align="center"><strong>4</strong></td>
<td align="left">덧셈/뺄셈</td>
<td align="left"><code>+</code> <code>-</code></td>
<td align="center">L → R</td>
</tr>
<tr>
<td align="center"><strong>5</strong></td>
<td align="left">비트 시프트</td>
<td align="left"><code>&lt;&lt;</code> <code>&gt;&gt;</code></td>
<td align="center">L → R</td>
</tr>
<tr>
<td align="center"><strong>6</strong></td>
<td align="left">관계</td>
<td align="left"><code>&lt;</code> <code>&gt;</code> <code>&lt;=</code> <code>&gt;=</code></td>
<td align="center">L → R</td>
</tr>
<tr>
<td align="center"><strong>7</strong></td>
<td align="left">등가</td>
<td align="left"><code>==</code> <code>!=</code></td>
<td align="center">L → R</td>
</tr>
<tr>
<td align="center"><strong>8</strong></td>
<td align="left">비트 AND</td>
<td align="left"><code>&amp;</code></td>
<td align="center">L → R</td>
</tr>
<tr>
<td align="center"><strong>9</strong></td>
<td align="left">비트 XOR</td>
<td align="left"><code>^</code></td>
<td align="center">L → R</td>
</tr>
<tr>
<td align="center"><strong>10</strong></td>
<td align="left">비트 OR</td>
<td align="left">`</td>
<td align="center">`</td>
</tr>
<tr>
<td align="center"><strong>11</strong></td>
<td align="left">논리 AND</td>
<td align="left"><code>&amp;&amp;</code></td>
<td align="center">L → R</td>
</tr>
<tr>
<td align="center"><strong>12</strong></td>
<td align="left">논리 OR</td>
<td align="left">`</td>
<td align="center"></td>
</tr>
<tr>
<td align="center"><strong>13</strong></td>
<td align="left">조건(삼항)</td>
<td align="left"><code>?:</code></td>
<td align="center">R → L</td>
</tr>
<tr>
<td align="center"><strong>14</strong></td>
<td align="left">대입</td>
<td align="left"><code>=</code> <code>+=</code> <code>-=</code> <code>*=</code> <code>/=</code> <code>%=</code> <code>&amp;=</code> <code>^=</code> `</td>
<td align="center">=<code></code>&lt;&lt;=<code></code>&gt;&gt;=`</td>
</tr>
<tr>
<td align="center"><strong>15 (최저)</strong></td>
<td align="left">쉼표(순차)</td>
<td align="left"><code>,</code></td>
<td align="center">L → R</td>
</tr>
</tbody></table>
<p>조금이라도 헷갈리면 무조건 괄호 <code>( )</code>를 사용하는것이 좋다.</p>
<p>괄호를 사용하면 우선순위가 가장 높기에 먼저 실행되고 가독성도 좋아진다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WEB / JSP] GET 방식과 POST 방식의 차이점]]></title>
            <link>https://velog.io/@kt_gml/WEBJSP-GET-%EB%B0%A9%EC%8B%9D%EA%B3%BC-POST-%EB%B0%A9%EC%8B%9D%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@kt_gml/WEBJSP-GET-%EB%B0%A9%EC%8B%9D%EA%B3%BC-POST-%EB%B0%A9%EC%8B%9D%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Tue, 01 Jul 2025 13:58:10 GMT</pubDate>
            <description><![CDATA[<p><strong>웹 프로그래밍 기초</strong> 수업을 수강하면서 <strong>JSP</strong>와 <strong>자바스크립트</strong>를 배웠다. </p>
<p>이때 데이터베이스 삽입, 삭제, 수정, 검색 그리고 쿠키나 세션을 활용한 로그인과 회원가입 로직을 구현해보며 1학기를 마무리했다.</p>
<hr>
<h2 id="get-방식과-post-방식의-차이점">GET 방식과 POST 방식의 차이점</h2>
<p>GET과 POST는 HTTP 프로토콜을 이용해 서버에 무언가를 요청할 때 사용하는 방식이다. 둘 다 요청을 보내는 역할을 하지만, 사용하는 목적과 방식에 큰 차이가 있다.</p>
<p><strong>엽서(GET)와 편지 봉투(POST)에 비유할 수 있다.</strong></p>
<p><strong>GET 방식은 <code>엽서</code>를 보내는 것과 비슷하다.</strong>
    주소(URL)에 전달할 내용(데이터)이 전부 드러나 있어 누구나 쉽게 볼 수 있다.</p>
<p><strong>POST 방식은 <code>편지 봉투</code>에 편지를 넣어 보내는 것과 같다.</strong>
    내용(데이터)을 봉투(Body) 안에 숨겨서 보내기 때문에, 겉에서는 내용을 알 수 없다.</p>
<hr>
<h3 id="주요-차이점-비교">주요 차이점 비교</h3>
<table>
<thead>
<tr>
<th align="left">항목</th>
<th align="left">GET</th>
<th align="left">POST</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>데이터 전송 위치</strong></td>
<td align="left">URL 뒤에 쿼리 스트링(Query String)으로 붙여서 전송하다. <br> <code>?key=value&amp;key2=value2</code> 형태.</td>
<td align="left">HTTP Request Body에 담아서 전송하다.</td>
</tr>
<tr>
<td align="left"><strong>보안</strong></td>
<td align="left">URL에 데이터가 그대로 노출되어 보안에 취약하다. <br> (서버 로그, 브라우저 기록에 남음)</td>
<td align="left">Body에 데이터를 담으므로 URL에 노출되지 않아 상대적으로 안전하다.</td>
</tr>
<tr>
<td align="left"><strong>데이터 길이</strong></td>
<td align="left">URL 길이에 제한이 있어 전송할 수 있는 데이터의 양이 적다.</td>
<td align="left">길이 제한이 거의 없어 대용량 데이터 전송이 가능하다.</td>
</tr>
<tr>
<td align="left"><strong>데이터 형태</strong></td>
<td align="left">주로 텍스트 데이터만 가능하다.</td>
<td align="left">텍스트, 파일(이미지, 영상 등) 다양한 형태의 데이터 전송이 가능하다.</td>
</tr>
<tr>
<td align="left"><strong>멱등성(Idempotency)*</strong></td>
<td align="left">멱등성을 가진다. (여러 번 요청해도 결과가 동일하다)</td>
<td align="left">멱등성을 가지지 않는다. (요청할 때마다 서버 상태가 바뀔 수 있다)</td>
</tr>
<tr>
<td align="left"><strong>캐싱(Caching)</strong></td>
<td align="left">브라우저가 결과를 캐싱(저장)할 수 있다. 그래서 속도가 빠를 수 있다.</td>
<td align="left">기본적으로 캐싱하지 않는다.</td>
</tr>
<tr>
<td align="left"><strong>북마크 / 방문 기록</strong></td>
<td align="left">북마크하거나 방문 기록에 남길 수 있다.</td>
<td align="left">북마크하거나 방문 기록에 남길 수 없다.</td>
</tr>
</tbody></table>
<hr>
<h3 id="언제-사용할까">언제 사용할까?</h3>
<h4 id="get을-사용하는-경우"><strong>GET을 사용하는 경우</strong></h4>
<p>서버에서 데이터를 <strong>조회(Read)</strong>할 때 주로 사용하다.</p>
<p>검색 엔진에서 검색어를 전달하는 것처럼, 데이터가 노출되어도 괜찮은 경우에 사용하다.</p>
<p>캐싱이 필요한 경우에 유리하다. (예: 이미지나 CSS 파일 요청)</p>
<h4 id="post를-사용하는-경우"><strong>POST를 사용하는 경우</strong></h4>
<p>서버에 데이터를 <strong>생성(Create)</strong>하거나 <strong>수정(Update)</strong>할 때 주로 사용하다.</p>
<p>로그인 정보나 회원가입 정보처럼 <strong>민감한 데이터를 전송</strong>할 때 사용하다.</p>
<p><strong>파일 업로드</strong>처럼 데이터의 크기가 클 때 사용하다.</p>
<hr>
<h3 id="jsp에서-post-방식을-사용하여-입력하기">JSP에서 POST 방식을 사용하여 입력하기</h3>
<pre><code class="language-jsp">input.jsp
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;form action=&quot;process.jsp&quot; method=&quot;post&quot;&gt;
이름입력:&lt;input type=&quot;text&quot; name=&quot;name&quot; size=&quot;10&quot;&gt;
점수입력:&lt;input type=&quot;text&quot; name=&quot;score&quot; size=&quot;10&quot;&gt;
&lt;input type=&quot;submit&quot; value=&quot;입력&quot;&gt;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3 id="jsp에서-get-방식으로-출력하기">JSP에서 GET 방식으로 출력하기</h3>
<pre><code class="language-jsp">output.jsp
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
입력한 사용자 아이디 :&lt;%=request.getParameter(&quot;userId&quot;) %&gt;&lt;br&gt;
입력한 사용자 패스워드 :&lt;%=request.getParameter(&quot;password&quot;) %&gt;&lt;br&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/fc837a18-16f4-4153-b6fa-3bcd56fecb2d/image.png" alt=""></p>
<p>주소창에 GET 방식으로 userId와 password를 전달하여서 결과가 잘 출력되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[디지털 논리회로 ]]></title>
            <link>https://velog.io/@kt_gml/%EB%94%94%EC%A7%80%ED%84%B8-%EB%85%BC%EB%A6%AC%ED%9A%8C%EB%A1%9C</link>
            <guid>https://velog.io/@kt_gml/%EB%94%94%EC%A7%80%ED%84%B8-%EB%85%BC%EB%A6%AC%ED%9A%8C%EB%A1%9C</guid>
            <pubDate>Tue, 01 Jul 2025 07:19:05 GMT</pubDate>
            <description><![CDATA[<h2 id="디지털-논리회로-총정리">디지털 논리회로 총정리</h2>
<p>컴퓨터 공학과 복수전공을 준비하며 타 학교 컴퓨터 공학과에서는 전공 필수 과목일 정도로 중요한 논리회로라는 강의를 수강하였다.</p>
<p>한 학기에 전부 공부하기엔 양이 많아 가볍게 보고 넘어간 부분들도 많다. </p>
<p>나중에 보면 내용이 떠오를 정도로 기억에 남는 핵심 내용들만 복습겸 정리해보겠다.</p>
<hr>
<h2 id="1-adc와-양자화-잡음quantization-noise">1. ADC와 양자화 잡음(Quantization Noise)</h2>
<p>ADC(Analog to Digital Converter)는 <strong>아날로그 신호(연속적인 전압, 전류 등)를 디지털 신호(0과 1의 조합)로 변환하는 장치</strong>다.<br>센서, 마이크, 온도계, 조이스틱 등에서 나오는 아날로그 데이터를 컴퓨터나 마이크로컨트롤러가 처리할 수 있도록 해주는 필수 인터페이스다.</p>
<h3 id="adc의-변환-과정">ADC의 변환 과정</h3>
<p>ADC는 아날로그 신호를 디지털로 바꾸기 위해 다음의 3단계 과정을 거친다.</p>
<ol>
<li><p><strong>샘플링(Sampling)</strong>  </p>
<ul>
<li>연속적인 아날로그 신호를 일정 시간 간격(샘플링 주기)마다 잘라서 이산적인 값(샘플)으로 만든다.  </li>
<li>예: 온도센서에서 1초에 100번씩 온도를 측정  </li>
<li>샘플링 주파수는 신호의 최대 주파수의 2배 이상(샤논의 표본화 정리)이어야 원 신호를 복원할 수 있다.</li>
</ul>
</li>
<li><p><strong>양자화(Quantization)</strong>  </p>
<ul>
<li>샘플링된 각 값을 미리 정해진 여러 단계(양자화 레벨) 중 가장 가까운 값으로 반올림한다.  </li>
<li>예: 8비트 ADC라면 0~255까지 256단계로 나눔  </li>
<li>이 과정에서 실제 값과 양자화된 값 사이에 오차(양자화 잡음)가 생긴다.</li>
</ul>
</li>
<li><p><strong>부호화(Encoding)</strong>  </p>
<ul>
<li>양자화된 값을 컴퓨터가 이해할 수 있는 2진수(비트열)로 변환한다.  </li>
<li>예: 150단계 → 10010110(2진수)</li>
</ul>
</li>
</ol>
<h3 id="아날로그-신호와-디지털-신호">아날로그 신호와 디지털 신호</h3>
<img src="https://velog.velcdn.com/images/kt_gml/post/9633e649-4762-44ab-8489-d1094e41989e/image.png" width="400px">


<p>쉽게 얘기해서 왼쪽과 같은 아날로그 신호를 일정 간격으로 자르는게 <strong>샘플링</strong>이고, 자른 값을 정해놓은 방식대로 반올림 하는게 <strong>양자화</strong> 이걸 2진수로 바꾸는것이 <strong>부호화</strong>이다. 그리고 이 양자화 과정의 반올림에서 생기는 오차가 바로 <strong>양자화 잡음</strong>이다. </p>
<p>양자화 잡음은 신호 품질을 떨어뜨리는 주요 원인 중 하나다.<br>양자화 스텝이 작을수록 잡음은 줄지만, 필요한 비트 수가 늘어나서 저장 공간이나 전송 대역폭이 커진다.</p>
<p><strong>양자화 잡음 줄이는 법</strong>
  비선형 압신(Companding) 사용: 작은 신호에 더 촘촘한 스텝을 할당한다.
  오버샘플링: 샘플링 주파수를 높여 잡음을 분산시킨다.</p>
<hr>
<h2 id="2-그레이-코드gray-code">2. 그레이 코드(Gray Code)</h2>
<p>그레이 코드는 연속된 값끼리 1비트만 다르게 만드는 이진 코드다.<br>이 코드를 쓰면 값이 바뀔 때 여러 비트가 동시에 바뀌는 걸 막을 수 있어서, 하드웨어에서 신호 오류를 줄일 수 있다.</p>
<p>XOR을 활용해 2진수를 그레이코드로 변환할 수 있다.</p>
<blockquote>
<p>*<em>XOR을 활용한 2진수 - 그레이코드 변환 *</em>
<a href="https://computersource.tistory.com/60">https://computersource.tistory.com/60</a></p>
</blockquote>
<p><strong>특징</strong>
  인접 값끼리 1비트만 다르다.
  회로에서 에러 검출, 엔코더, 디지털-아날로그 변환 등에 많이 쓴다.</p>
<p><strong>파이썬에서의 이진수  그레이 코드 변환</strong></p>
<pre><code class="language-python">  # 이진수 → 그레이 코드
  gray = binary ^ (binary &gt;&gt; 1)
  # 그레이 코드 → 이진수
  b = gray
  while gray &gt; 0:
      gray = gray &gt;&gt; 1
      b = b ^ gray</code></pre>
<p><strong>예시 표</strong></p>
<table>
<thead>
<tr>
<th>10진수</th>
<th>이진수</th>
<th>그레이 코드</th>
</tr>
</thead>
<tbody><tr>
<td>0</td>
<td>000</td>
<td>000</td>
</tr>
<tr>
<td>1</td>
<td>001</td>
<td>001</td>
</tr>
<tr>
<td>2</td>
<td>010</td>
<td>011</td>
</tr>
<tr>
<td>3</td>
<td>011</td>
<td>010</td>
</tr>
<tr>
<td>4</td>
<td>100</td>
<td>110</td>
</tr>
<tr>
<td>5</td>
<td>101</td>
<td>111</td>
</tr>
<tr>
<td>6</td>
<td>110</td>
<td>101</td>
</tr>
<tr>
<td>7</td>
<td>111</td>
<td>100</td>
</tr>
</tbody></table>
<hr>
<h2 id="3-에러-검출-코드">3. 에러 검출 코드</h2>
<p>디지털 신호에서 에러를 검출하기 위해 여러 가지 코드가 쓰인다.</p>
<table>
<thead>
<tr>
<th>코드 종류</th>
<th>구조</th>
<th>특징/원리</th>
</tr>
</thead>
<tbody><tr>
<td>2-out-of-5 코드</td>
<td>5비트, 2개의 1</td>
<td>1의 개수가 2개 아니면 에러</td>
</tr>
<tr>
<td>이중 5 코드</td>
<td>7비트</td>
<td>항상 2개의 1을 가짐</td>
</tr>
<tr>
<td>링 카운터 코드</td>
<td>10비트, 1개의 1</td>
<td>1의 위치로 상태 구분</td>
</tr>
</tbody></table>
<p>2-out-of-5 코드는 각 코드에 1이 2개만 들어가도록 해서, 1의 개수가 다르면 에러라고 판단한다.</p>
<hr>
<h2 id="4-논리-게이트logic-gates">4. 논리 게이트(Logic Gates)</h2>
<p>디지털 회로의 기본 단위는 논리 게이트다.<br>각 게이트는 입력 신호를 받아 정해진 논리 연산을 수행한다.</p>
<p><strong>논리게이트 논리식 및 회로기호</strong>  </p>
<blockquote>
<p>*<em>이미지 출처 *</em>: <a href="https://ko.wikipedia.org/wiki/%EB%85%BC%EB%A6%AC_%ED%9A%8C%EB%A1%9C">위키백과</a>
<img src="https://velog.velcdn.com/images/kt_gml/post/53bfee5c-f76f-4e1e-97eb-08b06d63eb89/image.png" alt=""></p>
</blockquote>
<p><strong>버퍼(Buffer)</strong> 게이트는 입력 신호를 그대로 출력해서 신호를 안정화시키는 역할을 한다.</p>
<hr>
<h2 id="5-불대수boolean-algebra와-최소항식">5. 불대수(Boolean Algebra)와 최소항식</h2>
<p>불대수는 0과 1 두 값으로 논리 연산을 다루는 대수 체계다.<br>논리회로를 간소화할 때 꼭 필요한 도구다.</p>
<p><strong>주요 법칙</strong>
  교환법칙: A+B = B+A, A·B = B·A
  결합법칙: (A+B)+C = A+(B+C)
  분배법칙: A·(B+C) = A·B + A·C
  드모르간 법칙: (A+B)&#39; = A&#39;·B&#39;, (A·B)&#39; = A&#39;+B&#39;
  흡수법칙: A + A·B = A</p>
<h3 id="최소항minterm의-정의">최소항(minterm)의 정의</h3>
<p><strong>n개의 변수</strong>로 이루어진 논리식에서, 각 변수마다 한 번씩 등장하는 <strong>n개의 논리곱(AND) 항</strong>을 최소항이라고 한다.</p>
<p>각 최소항은 진리표의 한 행(입력 조합)에 정확히 하나씩 대응한다.</p>
<p>입력 조합이 1(참)이 되는 경우의 항만을 모아 OR(+) 연산으로 합친 것이 최소항식이다.</p>
<h4 id="예시">예시</h4>
<p>변수 x, y, z가 있을 때:
x=0, y=1, z=0에 해당하는 최소항:<br>  → x&#39; y z&#39;<br>x=1, y=0, z=1에 해당하는 최소항:<br>  → x y&#39; z</p>
<p>진리표에서 함수 f가 1이 되는 행의 입력 조합에 해당하는 최소항만을 OR로 연결한다.</p>
<h3 id="최소항식의-표기와-의미">최소항식의 표기와 의미</h3>
<p><strong>기호 표기</strong>:  
 m0, m1, m2, ...로 표기한다.<br> 예: x&#39;y&#39;z = m1 (x=0, y=0, z=1)</p>
<p> <strong>함수 표현</strong>:  
  F(x, y, z) = Σ(1, 3, 6, 7)<br>    → m1, m3, m6, m7에 해당하는 최소항의 합</p>
<h4 id="예시-1">예시</h4>
<p>f = x&#39;y&#39;z + x&#39;yz + xy&#39;z&#39; + xyz
이 식은 각 항이 세 변수(x, y, z)를 모두 한 번씩 포함하므로 최소항식이다.</p>
<h3 id="최소항식-전개의-방법">최소항식 전개의 방법</h3>
<ol>
<li><strong>진리표 작성</strong><br>입력 변수의 모든 조합에 대해 함수 값(f)이 1인 행을 찾는다.</li>
<li><strong>각 조합에 해당하는 최소항 도출</strong><br>0인 변수는 보수(NOT), 1인 변수는 그대로 사용하여 AND로 묶는다.</li>
<li><strong>모든 최소항을 OR로 연결</strong><br>f가 1인 모든 행의 최소항을 + 연산(OR)으로 합친다.</li>
</ol>
<h4 id="예시-2">예시</h4>
<p>변수 3개(x, y, z), f가 1인 경우:<br>(x, y, z) = (0, 1, 1) → x&#39; y z<br>(x, y, z) = (1, 0, 0) → x y&#39; z&#39;<br>(x, y, z) = (1, 1, 0) → x y z&#39;<br>최소항식: f = x&#39;y z + x y&#39; z&#39; + x y z&#39;</p>
<h3 id="최소항식의-특징과-활용">최소항식의 특징과 활용</h3>
<p><strong>논리회로 설계의 표준</strong>:  
  논리함수를 AND, OR 게이트만으로 간단하게 구현할 수 있다.
<strong>카르노맵, 퀸-맥클러스키 등 논리식 간소화의 출발점</strong>이 된다.
<strong>모든 최소항을 조합하면 2ⁿ개</strong>가 만들어진다(n: 변수 개수)</p>
<h3 id="최소항과-최대항의-관계">최소항과 최대항의 관계</h3>
<p><strong>최소항(Minterm)</strong><br>  함수가 1이 되는 입력 조합의 논리곱(AND)
<strong>최대항(Maxterm)</strong><br>  함수가 0이 되는 입력 조합의 논리합(OR)
<strong>보수 관계</strong>
  최소항식의 보수는 최대항식, 최대항식의 보수는 최소항식이 된다.</p>
<hr>
<h2 id="6-카르노맵karnaugh-map">6. 카르노맵(Karnaugh Map)</h2>
<p>카르노맵은 불 함수를 시각적으로 간소화하는 도구다.<br>입력 변수가 3~4개일 때 효과적으로 최소 논리식을 찾을 수 있다.</p>
<p><strong>3변수 예시</strong></p>
<table>
<thead>
<tr>
<th>X\YZ</th>
<th>00</th>
<th>01</th>
<th>11</th>
<th>10</th>
</tr>
</thead>
<tbody><tr>
<td>0</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>0</td>
</tr>
<tr>
<td>1</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>0</td>
</tr>
</tbody></table>
<p>1이 인접한 칸끼리 묶어서 최소항을 만든다.</p>
<p><strong>묶음 예시</strong>
  X=0, YZ=00/01 → $\overline{X}\overline{Y}$
  X=0, YZ=01/11 → $\overline{X}Z$</p>
<p><strong>최종식</strong>
$\overline{X}\overline{Y} + \overline{X}Z$</p>
<blockquote>
<p><strong>카르노맵 간소화 방법</strong>
<a href="https://homubee.tistory.com/42">https://homubee.tistory.com/42</a></p>
</blockquote>
<hr>
<h2 id="7-퀸-맥클러스키quine-mccluskey-방법">7. 퀸-맥클러스키(Quine-McCluskey) 방법</h2>
<p><strong>변수 4개 이상의 논리식 간소화</strong>를 위한 체계적 알고리즘이다. 카르노맵보다 확장성이 높아 디지털 컴퓨터 구현에 적합하다.  </p>
<h3 id="동작-원리"><strong>동작 원리</strong></h3>
<ol>
<li><p><strong>최소항 그룹화</strong>:  
최소항을 2진수로 변환 후 <strong>1의 개수</strong>로 그룹 분류<br>EX: 최소항 <code>[0(000), 1(001), 2(010), 5(101), 6(110), 7(111)]</code><br>  그룹 0: <code>000</code><br>  그룹 1: <code>001</code>, <code>010</code><br>  그룹 2: <code>101</code>, <code>110</code><br>  그룹 3: <code>111</code>  </p>
</li>
<li><p><strong>주항(PI) 추출</strong>:  
인접 그룹끼리 <strong>1비트 차이</strong> 항 병합<br>  <code>000-001</code> → <code>00-</code><br>  <code>010-110</code> → <code>-10</code><br>  <code>101-111</code> → <code>1-1</code><br>더 이상 병합 불가능한 항을 <strong>주항</strong>으로 선택  </p>
</li>
<li><p><strong>주항 차트로 최소 커버링</strong>:  
필수 주항(Essential PI) 선정 후 나머지 항 커버링.</p>
</li>
</ol>
<blockquote>
<p><strong>퀸 맥클러스키 간소화 방법</strong>
<a href="https://blog.naver.com/leeyunghuk1/220959424842">https://blog.naver.com/leeyunghuk1/220959424842</a></p>
</blockquote>
<hr>
<h2 id="8-가산기adder">8. 가산기(Adder)</h2>
<p>조합논리회로는 AND, OR, NOT 세가지 기본 논리회로의 조합으로 만들어지고 가산기에 대해 정리하겠다.</p>
<h3 id="반가산기half-adder"><strong>반가산기(Half Adder)</strong></h3>
<p>반가산기는 한 자리 2진수 2개를 입력해서 합과 캐리를 계산하는 회로이다.</p>
<p><strong>입력</strong>: A, B
<strong>출력</strong>: 합(S), C </p>
<table>
<thead>
<tr>
<th align="center">A</th>
<th align="center">B</th>
<th align="center">S</th>
<th align="center">C</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">1</td>
</tr>
</tbody></table>
<img src="https://velog.velcdn.com/images/kt_gml/post/f42f1337-711f-4654-97a0-64f944a00627/image.png" width="200px">



<h3 id="전가산기full-adder"><strong>전가산기(Full Adder)</strong></h3>
<p>반 가산기는 2진수 한 자리 덧셈을 하기때문에 아랫자리에서 발생한 올림은 고려하지 않아 2비트 이상의 2진수 덧셈은 할 수 없다.</p>
<p>그러하여 이전 자리올림을 입력으로 넣어 덧셈을 할 수 있도록 한 회로가 전가산기이다.</p>
<ul>
<li><strong>입력</strong>: A, B, 이전 자리올림(Cin)  </li>
<li><strong>출력</strong>: 합(S), Cout</li>
</ul>
<table>
<thead>
<tr>
<th align="center">A</th>
<th align="center">B</th>
<th align="center">Ci</th>
<th align="center">S</th>
<th align="center">Co</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">1</td>
</tr>
</tbody></table>
<img src="https://velog.velcdn.com/images/kt_gml/post/7cd61363-20a8-4a77-8d72-c57581a06602/image.png" width="400px">


<hr>
<h2 id="9-디코더decoder">9. 디코더(Decoder)</h2>
<p>디코더는 <strong>n비트의 입력 → 2^n개의 정보</strong>로 출력시켜주는 회로이다. </p>
<p><strong>주소 지정</strong>과 <strong>명령어 해석</strong>에 사용한다.  </p>
<p>2x4 디코더의 경우에는 아래와 같은 진리표, 논리식, 회로도를 가진다.</p>
<p>아래와 같이 표와 논리식을 보기 좋게 정리할 수 있다.</p>
<h4 id="입력과-출력-진리표">입력과 출력 진리표</h4>
<table>
<thead>
<tr>
<th align="center">B</th>
<th align="center">A</th>
<th align="center">Y₃</th>
<th align="center">Y₂</th>
<th align="center">Y₁</th>
<th align="center">Y₀</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
</tr>
</tbody></table>
<h4 id="각-출력의-논리식">각 출력의 논리식</h4>
<p>$$Y_0 = \overline{B} \overline{A}$$
$$Y_1 = \overline{B} A$$
$$Y_2 = B \overline{A}$$
$$Y_3 = B A$$</p>
<img src="https://velog.velcdn.com/images/kt_gml/post/e35f505a-b43f-4ba9-86e0-7cad153708eb/image.png" width="300px">


<hr>
<h2 id="10-멀티플렉서multiplexer-mux">10. 멀티플렉서(Multiplexer, MUX)</h2>
<p>멀티플렉서는 <strong>2^n개의입력 중 1개 선택해서 출력하는 조합논리회로</strong>. </p>
<p>데이터 선택기로 CPU 내부에서 활용한다.  </p>
<h3 id="4x1-mux"><strong>4X1 MUX</strong></h3>
<table>
<thead>
<tr>
<th align="center">S₁</th>
<th align="center">S₀</th>
<th align="center">F</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">D₀</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">D₁</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">D₂</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">D₃</td>
</tr>
</tbody></table>
<img src="https://velog.velcdn.com/images/kt_gml/post/966d886f-e064-4897-b9bd-5f8abe23ea2d/image.png" width="300px">



<h3 id="4x1-mux를-이용해-3개의-입력에-따른-출력-설계하기"><strong>4X1 MUX를 이용해 3개의 입력에 따른 출력 설계하기</strong></h3>
<p>$$F(A, B, C) = \Sigma m(0, 1, 5, 7)$$</p>
<table>
<thead>
<tr>
<th align="center">A</th>
<th align="center">B</th>
<th align="center">C</th>
<th align="center">D₀</th>
<th align="center">D₁</th>
<th align="center">D₂</th>
<th align="center">D₃</th>
<th align="center">F</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">1</td>
</tr>
</tbody></table>
<br>

<table>
<thead>
<tr>
<th align="center">A</th>
<th align="center">B</th>
<th align="center">C</th>
<th align="center">F</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">D₀ = 1</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">D₀ = 1</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">D₁ = 0</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">D₁ = 0</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">D₂ = C</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">D₂ = C</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">D₃ = C</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">D₃ = C</td>
</tr>
</tbody></table>
<p>이 내용은 처음에 접했을때 어려웠지만 반복해서 연습하다보니 쉽게 느껴졌다.</p>
<p>C를 0,1과 같은 패턴으로 두고 패턴을 찾아 입력으로 사용하여 설계하면 된다.</p>
<hr>
<h2 id="11-래치latch와-플립플롭flip-flop">11. 래치(Latch)와 플립플롭(Flip-Flop)</h2>
<p><strong>순서논리회로</strong>는 기억 능력이 있는 논리회로인데, 이때 기억 능력을 위해 기억 소자가 필요하고 이때 사용되는 핵심 부품이 래치와 플립플롭이다.</p>
<p>플립플롭은 1비트의 정보를 저장할 수 있는 메모리 소자로, 저장 기능이 있다. </p>
<h3 id="래치latch와-플립플롭flip-flop의-비교"><strong>래치(Latch)와 플립플롭(Flip-Flop)의 비교</strong></h3>
<table>
<thead>
<tr>
<th align="left">특성</th>
<th align="left">래치 (Latch)</th>
<th align="left">플립플롭 (Flip-Flop)</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>동작 방식</strong></td>
<td align="left"><strong>비동기식 (Asynchronous)</strong></td>
<td align="left"><strong>동기식 (Synchronous)</strong></td>
</tr>
<tr>
<td align="left"><strong>트리거 방식</strong></td>
<td align="left"><strong>레벨 트리거 (Level-Triggered)</strong></td>
<td align="left"><strong>엣지 트리거 (Edge-Triggered)</strong></td>
</tr>
<tr>
<td align="left"><strong>설명</strong></td>
<td align="left">입력 신호가 <strong>유지되는 동안</strong> 계속 출력이 변할 수 있음 (Enable 신호가 High인 동안 투명하게 동작)</td>
<td align="left">클럭 신호가 <strong>변하는 순간</strong>(상승 엣지 또는 하강 엣지)에만 출력이 변함</td>
</tr>
<tr>
<td align="left"><strong>안정성</strong></td>
<td align="left">낮음 (Race Condition 발생 가능성)</td>
<td align="left"><strong>높음</strong> (예측 가능한 시점에만 상태 변경)</td>
</tr>
</tbody></table>
<h3 id="1-sr-플립플롭-set-reset-flip-flop"><strong>1. SR 플립플롭 (Set-Reset Flip-Flop)</strong></h3>
<p>가장 기본적인 플립플롭으로, SR 래치에 클럭 입력을 추가한 구조이다.</p>
<p><strong>특성표 (Characteristic Table)</strong>
클럭이 인가되었을 때의 동작을 나타낸다.</p>
<table>
<thead>
<tr>
<th align="center">S</th>
<th align="center">R</th>
<th align="center"><strong>Q(t+1)</strong></th>
<th align="left">동작</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0</td>
<td align="center">0</td>
<td align="center"><strong>Q(t)</strong></td>
<td align="left">유지 (Hold)</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">1</td>
<td align="center"><strong>0</strong></td>
<td align="left">리셋 (Reset)</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">0</td>
<td align="center"><strong>1</strong></td>
<td align="left">셋 (Set)</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">1</td>
<td align="center"><strong>불확실 (Invalid)</strong></td>
<td align="left">금지 (Forbidden)</td>
</tr>
</tbody></table>
<p><strong>특성식 (Characteristic Equation)</strong>
$$Q_{t+1} = S + R&#39;Q$$ (단, SR=0 조건)</p>
<blockquote>
<p><strong>특성(방정)식</strong>은 특성표를 바탕으로 만든 카르노맵을 통해 구할 수 있다.</p>
</blockquote>
<p><strong>여기표 (Excitation Table)</strong>
현재 상태 <code>Q(t)</code>에서 다음 상태 <code>Q(t+1)</code>로 변하기 위해 필요한 입력 <code>S</code>와 <code>R</code>을 나타낸다. (X는 Don&#39;t Care)</p>
<table>
<thead>
<tr>
<th align="center">Q(t)</th>
<th align="center">Q(t+1)</th>
<th align="center">S</th>
<th align="center">R</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">X</td>
<td align="left">상태를 0으로 유지(Hold)하거나 리셋(Reset)</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="left">반드시 셋(Set) 해야 함</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="left">반드시 리셋(Reset) 해야 함</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">X</td>
<td align="center">0</td>
<td align="left">상태를 1로 유지(Hold)하거나 셋(Set)</td>
</tr>
<tr>
<td align="center">- <strong>문제점</strong>: S=R=1일 때 출력이 불안정해지는 금지된 상태가 존재한다.</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="left"></td>
</tr>
</tbody></table>
<h3 id="2-d-플립플롭-data-flip-flop"><strong>2. D 플립플롭 (Data Flip-Flop)</strong></h3>
<p>SR 플립플롭의 S=R=1 문제를 해결하기 위해 입력을 하나로 통합한 플립플롭이다.</p>
<p><strong>구조</strong>
SR 플립플롭의 S 입력에 D를, R 입력에 D의 NOT 게이트를 연결합니다. 이로써 S와 R은 항상 반대 값을 갖게 되어 S=R=1 조건이 원천적으로 차단된다.</p>
<p><strong>동작</strong>
클럭의 엣지에서 입력 <code>D</code>의 값이 그대로 출력 <code>Q</code>에 저장된다. 그래서 &#39;데이터&#39; 또는 &#39;지연(Delay)&#39; 플립플롭이라고도 한다.</p>
<p><strong>특성표</strong></p>
<table>
<thead>
<tr>
<th align="center">D</th>
<th align="center"><strong>Q(t+1)</strong></th>
<th align="left">동작</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0</td>
<td align="center"><strong>0</strong></td>
<td align="left">리셋 (Reset)</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center"><strong>1</strong></td>
<td align="left">셋 (Set)</td>
</tr>
</tbody></table>
<p><strong>특성식</strong>
$$Q_{t+1} = D$$</p>
<p><strong>여기표</strong></p>
<table>
<thead>
<tr>
<th align="center">Q(t)</th>
<th align="center">Q(t+1)</th>
<th align="center">D</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="left">다음 상태가 0이므로 D도 0이어야 함</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="left">다음 상태가 1이므로 D도 1이어야 함</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="left">다음 상태가 0이므로 D도 0이어야 함</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="left">다음 상태가 1이므로 D도 1이어야 함</td>
</tr>
</tbody></table>
<h3 id="3-jk-플립플롭-jack-kilby-flip-flop"><strong>3. JK 플립플롭 (Jack-Kilby Flip-Flop)</strong></h3>
<p>SR 플립플롭의 S=R=1 문제를 해결하면서, 그 상태를 유용한 <strong>토글(Toggle)</strong> 기능으로 활용한 범용 플립플롭이다.</p>
<p><strong>진화</strong>
S=J, R=K로 대응되며, J=K=1일 때 출력이 현재 상태의 반대(Q&#39;)가 된다.</p>
<p><strong>특성표</strong></p>
<table>
<thead>
<tr>
<th align="center">J</th>
<th align="center">K</th>
<th align="center"><strong>Q(t+1)</strong></th>
<th align="left">동작</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0</td>
<td align="center">0</td>
<td align="center"><strong>Q(t)</strong></td>
<td align="left">유지 (Hold)</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">1</td>
<td align="center"><strong>0</strong></td>
<td align="left">리셋 (Reset)</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">0</td>
<td align="center"><strong>1</strong></td>
<td align="left">셋 (Set)</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">1</td>
<td align="center"><strong>Q&#39;(t)</strong></td>
<td align="left"><strong>토글 (Toggle)</strong></td>
</tr>
</tbody></table>
<p><strong>특성식</strong>
$$Q_{t+1} = JQ&#39; + K&#39;Q$$</p>
<p><strong>여기표</strong></p>
<table>
<thead>
<tr>
<th align="center">Q(t)</th>
<th align="center">Q(t+1)</th>
<th align="center">J</th>
<th align="center">K</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">X</td>
<td align="left">0으로 유지(Hold)하거나 리셋(Reset)</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">X</td>
<td align="left">1로 셋(Set)하거나 토글(Toggle)</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">X</td>
<td align="center">1</td>
<td align="left">0으로 리셋(Reset)하거나 토글(Toggle)</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">X</td>
<td align="center">0</td>
<td align="left">1로 유지(Hold)하거나 셋(Set)</td>
</tr>
</tbody></table>
<h3 id="4-t-플립플롭-toggle-flip-flop"><strong>4. T 플립플롭 (Toggle Flip-Flop)</strong></h3>
<p>JK 플립플롭의 J와 K 입력을 하나로 묶어 만든 특수 형태의 플립플롭이다.</p>
<p><strong>구조</strong>
J=K=T로 연결한다.</p>
<p><strong>동작</strong>
T=0이면 현재 상태를 유지하고, T=1이면 클럭 엣지마다 출력을 반전(토글)시킨다. 주파수를 1/2로 분주하는 효과가 있어 <strong>카운터</strong> 설계의 핵심 소자이다.</p>
<p><strong>특성표</strong></p>
<table>
<thead>
<tr>
<th align="center">T</th>
<th align="center"><strong>Q(t+1)</strong></th>
<th align="left">동작</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0</td>
<td align="center"><strong>Q(t)</strong></td>
<td align="left">유지 (Hold)</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center"><strong>Q&#39;(t)</strong></td>
<td align="left"><strong>토글 (Toggle)</strong></td>
</tr>
</tbody></table>
<p><strong>특성식</strong> 
$$Q_{t+1} = T \oplus Q = TQ&#39; + T&#39;Q$$</p>
<p><strong>여기표</strong></p>
<table>
<thead>
<tr>
<th align="center">Q(t)</th>
<th align="center">Q(t+1)</th>
<th align="center">T</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="left">상태가 유지되었으므로 T=0</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="left">상태가 반전되었으므로 T=1</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="left">상태가 반전되었으므로 T=1</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="left">상태가 유지되었으므로 T=0</td>
</tr>
</tbody></table>
<br>

<hr>
<h2 id="12-동기-순서논리회로-설계-3비트-동기식-카운터-jk-플립플롭-사용">12. 동기 순서논리회로 설계: 3비트 동기식 카운터 (JK 플립플롭 사용)</h2>
<h3 id="설계-5단계-상세-과정"><strong>설계 5단계 상세 과정</strong></h3>
<p><strong>목표</strong>
000 → 001 → 010 → 011 → 100 → 101 → 110 → 111 → 000... 순서로 카운트하는 3비트 동기식 카운터 설계</p>
<h4 id="1단계-상태도-state-diagram"><strong>1단계: 상태도 (State Diagram)</strong></h4>
<p>3비트이므로 총 $2^3 = 8$개의 상태(000 ~ 111)를 가진다.</p>
<p>각 상태는 클럭이 한 번 인가될 때마다 다음 숫자로 변경된다. 
(예: 010에서 클럭 인가 시 011로)</p>
<p>마지막 상태인 111에서는 다음 클럭에 000으로 돌아간다.</p>
<p><code>(000) -&gt; (001) -&gt; (010) -&gt; (011) -&gt; (100) -&gt; (101) -&gt; (110) -&gt; (111) -&gt; (000)</code></p>
<h4 id="2단계-상태표-state-table"><strong>2단계: 상태표 (State Table)</strong></h4>
<table>
<thead>
<tr>
<th align="center">현재 상태 (A B C)</th>
<th align="center">다음 상태 (A B C)</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0 0 0</td>
<td align="center">0 0 1</td>
</tr>
<tr>
<td align="center">0 0 1</td>
<td align="center">0 1 0</td>
</tr>
<tr>
<td align="center">0 1 0</td>
<td align="center">0 1 1</td>
</tr>
<tr>
<td align="center">0 1 1</td>
<td align="center">1 0 0</td>
</tr>
<tr>
<td align="center">1 0 0</td>
<td align="center">1 0 1</td>
</tr>
<tr>
<td align="center">1 0 1</td>
<td align="center">1 1 0</td>
</tr>
<tr>
<td align="center">1 1 0</td>
<td align="center">1 1 1</td>
</tr>
<tr>
<td align="center">1 1 1</td>
<td align="center">0 0 0</td>
</tr>
</tbody></table>
<h4 id="3단계-상태-여기표-excitation-table"><strong>3단계: 상태 여기표 (Excitation Table)</strong></h4>
<p>2단계 상태표와 JK 플립플롭의 여기표를 결합</p>
<table>
<thead>
<tr>
<th align="center">현재 상태</th>
<th align="center">다음 상태</th>
<th align="center">JA</th>
<th align="center">KA</th>
<th align="center">JB</th>
<th align="center">KB</th>
<th align="center">JC</th>
<th align="center">KC</th>
</tr>
</thead>
<tbody><tr>
<td align="center">A B C</td>
<td align="center">A B C</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">---</td>
<td align="center">---</td>
<td align="center">---</td>
<td align="center">---</td>
<td align="center">---</td>
<td align="center">---</td>
<td align="center">---</td>
<td align="center">---</td>
</tr>
<tr>
<td align="center">0 0 0</td>
<td align="center">0 0 1</td>
<td align="center">0</td>
<td align="center">×</td>
<td align="center">0</td>
<td align="center">×</td>
<td align="center">1</td>
<td align="center">×</td>
</tr>
<tr>
<td align="center">0 0 1</td>
<td align="center">0 1 0</td>
<td align="center">0</td>
<td align="center">×</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">×</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">0 1 0</td>
<td align="center">0 1 1</td>
<td align="center">0</td>
<td align="center">×</td>
<td align="center">×</td>
<td align="center">0</td>
<td align="center">×</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">0 1 1</td>
<td align="center">1 0 0</td>
<td align="center">1</td>
<td align="center">×</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">×</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">1 0 0</td>
<td align="center">1 0 1</td>
<td align="center">×</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">×</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">1 0 1</td>
<td align="center">1 1 0</td>
<td align="center">×</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">×</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">1 1 0</td>
<td align="center">1 1 1</td>
<td align="center">×</td>
<td align="center">0</td>
<td align="center">×</td>
<td align="center">0</td>
<td align="center">×</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">1 1 1</td>
<td align="center">0 0 0</td>
<td align="center">×</td>
<td align="center">1</td>
<td align="center">×</td>
<td align="center">1</td>
<td align="center">×</td>
<td align="center">×</td>
</tr>
</tbody></table>
<blockquote>
<p>‘×’는 무관(상관없음) 조건을 의미한다.</p>
</blockquote>
<h4 id="4단계-카르노맵을-이용한-입력-논리식-간소화"><strong>4단계: 카르노맵을 이용한 입력 논리식 간소화</strong></h4>
<p>3단계 표를 바탕으로 각 J, K 입력(J2, K2, J1, K1, J0, K0)에 대한 논리식을 카르노맵을 이용해 구한다.</p>
<p>JA = BC
KA = BC
JB = C
KB = C
JC = 1
KC = 1</p>
<h4 id="5단계-회로도-구현-circuit-implementation"><strong>5단계: 회로도 구현 (Circuit Implementation)</strong></h4>
<p>4단계에서 구한 논리식을 바탕으로 회로를 구성한다.</p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/67e29dec-aea9-48b5-9f67-1fde2b54dc9c/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DataBase] 인력 필요량 예측 및 배치 시스템 데이터베이스 설계]]></title>
            <link>https://velog.io/@kt_gml/%EC%8B%9C%ED%9D%A5%EC%8B%9C-%EC%9E%90%EC%98%81%EC%97%85%EC%9E%90%EC%9D%98-%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%EC%9D%B8%EB%A0%A5%EB%B0%B0%EC%B9%98%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EA%B5%AC%EC%B6%95</link>
            <guid>https://velog.io/@kt_gml/%EC%8B%9C%ED%9D%A5%EC%8B%9C-%EC%9E%90%EC%98%81%EC%97%85%EC%9E%90%EC%9D%98-%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%EC%9D%B8%EB%A0%A5%EB%B0%B0%EC%B9%98%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EA%B5%AC%EC%B6%95</guid>
            <pubDate>Mon, 30 Jun 2025 09:22:24 GMT</pubDate>
            <description><![CDATA[<h1 id="인력-필요량-예측-및-인력-배치-시스템">인력 필요량 예측 및 인력 배치 시스템</h1>
<p>이 프로젝트는 대학교 <strong>&#39;데이터베이스설계&#39;</strong> 강의에서 진행하였다.</p>
<p>한 학기동안 개념 학습, 시험 공부, 매주 진행하는 팀 활동으로 많은 노력을 쏟았다. </p>
<p>그 결과 약 70명중 1-2등으로 <strong>A+</strong>라는 성적을 받았다. </p>
<p>프로젝트의 주제는 <strong>&quot;데이터베이스를 활용한 시흥시의 불편함 개선&quot;</strong>이었고 이때 사용자가 현업에서 실제로 사용할 수 있는 데이터베이스를 설계해야했다.</p>
<p>이 강의와 프로젝트를 통해 효율적인 ERD 설계를 중심으로 정규화나 각종 데이터베이스에 대한 지식을 배웠다.
또한 SQL을 배우고 구축한 데이터베이스에 샘플데이터를 삽입해 SQL문을 통한 시연까지 해보았다.</p>
<p>프로젝트는 다음과 같은 과정으로 진행되었다.</p>
<ol>
<li><p>인터뷰 요청</p>
</li>
<li><p>인터뷰를 통한 업무 및 요구사항 분석</p>
</li>
<li><p>ERD 설계(논리적 설계) 및 보완</p>
</li>
<li><p>용어사전 및 도메인 기술서 작성</p>
</li>
<li><p>물리적 설계</p>
</li>
<li><p>발표 및 보고서 작성
물리적 설계를 통한 테이블에 샘플 데이터 삽입 및 SQL문 시연</p>
</li>
</ol>
<p><br><br></p>
<h2 id="프로젝트-개요">프로젝트 개요</h2>
<p>학교 주변의 주점과 음식점은 우리 학교 학생들이 대부분의 손님을 차지한다.</p>
<p>이 점으로 학교 행사, 시험기간, 동아리 활동과 같은 교내의 요인들로 인해 사람들이 몰리는 시간대와 요일이 급격하게 변화한다.</p>
<p>이러한 점으로 사람이 없을 줄 알고 사장님 혼자서 홀과 주방을 모두 담당하시다가 손님이 몰려와서 안주가 나오는데 1시간 가까이 걸렸던 적도, 주변 학생들 중에서도 알바를 하다가 손님이 없어 어쩔 수 없이 조기 퇴근하는 경우도 보았다.</p>
<p>또한 사장님께서 여러 인력을 여러 시간대에 매일 카카오톡 단톡방으로 배치하는데, 어려움을 느끼신다고 하셨다.</p>
<p>이런 배경속에서 학교 행사나 여러 요소들을 동시에 관리해서 필요한 인력을 예측할 수 있는 인력 배치 시스템이 있다면 아래의 3가지 주체에게 각각 좋은 영향을 미칠 수 있을 것이라고 생각했다.</p>
<p><strong>1. 자영업자</strong>
인건비 절감
효율적인 식자재 관리
인력을 쉽게 관리</p>
<p><strong>2. 직원(알바생)</strong>
근로 시간 예측 가능
급여 안정성 증가</p>
<p><strong>3. 시흥시 주민(시설 이용자)</strong>
빠른 서비스(EX : 서빙 시간)
질 좋은 서비스(EX: 음식의 신선도)</p>
<h3 id="주제의-구체화">주제의 구체화</h3>
<p>나는 학교 앞 번화가의 <strong>가장 큰 주점</strong>을 주제로 선정했다.</p>
<p>처음에는 시흥시 자영업자의 효율적인 인력배치 시스템을 주제로 선정하려했지만 좁은 주제로 시작해 나중에 확장하는것이 더욱 효과적일 것이라고 생각했다.</p>
<p>예를 들면 어떤 자영업이든 필요인력의 정도를 알 수 있는 인력배치 시스템을 막연하게 주제로 시작하기보다는 학교라는 변수에 가장 민감하게 변화하는 학교 앞 번화가를 대상으로 시작해서 점차 데이터베이스를 확장해나가는게 효율적이라고 생각했다.</p>
<p>실제로 우리 학교 앞 번화가가 시흥에서 가장 큰 상권 중 하나였고, 주변 자영업들의 매출변동은 비슷한 구조를 띤다. </p>
<p>이때 매출이 가장 민감하게 변화하는 대형 주점을 주제로 삼는다면 인근 자영업으로도 시흥시로도 주제를 확장하기 유리하다고 생각했다.</p>
<p><br><br></p>
<h2 id="인터뷰-진행">인터뷰 진행</h2>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/71e00501-d705-4241-af86-d5761d190a95/image.png" alt=""></p>
<p>주제를 구체화 시킨 뒤 목표했던 대형 주점에 대한 인터뷰 내용을 설계하고 사장님께 인터뷰를 요청드렸고, 감사하게도 인터뷰에 응해주시기로 하였다.</p>
<p>인터뷰를 진행하며 우리가 생각했던 학생들에 의한 수요 변동에 대한 부분이 맞음을 확인할 수 있었다. </p>
<p>또한 현재 업무가 어떤식으로 진행되고 있는지, 시스템에 어떤 기능이 있으면 좋을지 등을 분석하는 시간을 가졌다.</p>
<br>

<h2 id="dfd-level-0data-flow-diagram-설계">DFD Level 0(Data Flow Diagram) 설계</h2>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/4d652e0d-e1d8-4fd6-ae86-aabfec646c35/image.png" alt=""></p>
<p>인터뷰를 바탕으로 ERD 작성을 위한 업무 기능도를 작성하였다.</p>
<p>또한 업무 기술서와 업무 기능도를 작성하여 ERD 작성을 위한 기반을 마련하였다.</p>
<h2 id="erd-설계논리적-설계">ERD 설계(논리적 설계)</h2>
<p> <img src="https://velog.velcdn.com/images/kt_gml/post/51829e31-ce99-4ccd-8b71-1d2abd53737d/image.png" alt=""></p>
<p>처음에는 위와 같이 도메인 없이 <strong>피터 첸(Peter-Chen) 표기법</strong>을 통해 ERD를 작성하여 중간 발표를 하였다.</p>
<p>이때 교수님께 피드백을 받았고 또한 개체, 관계, 속성의 검토에 대해서 배우고, 불필요함에도 불구하고 있으면 더 좋은 요소인 비정규화 등 많은 개념을 배웠다. </p>
<p>이러한 배움을 통해 처음엔 막연하게 그리던 ERD가 이젠 왜 필요한지를 이해하게 되면서 개체, 관계, 속성 하나하나 정말 많은 고민을 하면서 수정과 수정을 거듭하였다. </p>
<p>그러하여 용어사전과 도메인을 설계하고 최종발표때는 Toad Data Modeler 툴을 활용해 아래와 같은 최종 ERD를 설계할 수 있었다.</p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/88e3f673-12d7-44b0-be17-62429ddf1bb6/image.png" alt=""></p>
<p><br><br></p>
<h2 id="용어사전과-도메인-설계">용어사전과 도메인 설계</h2>
<p>협업을 피해갈 수 없는 실무에서 용어의 통일은 정말 중요하다고 교수님께서 말씀해주셨다.</p>
<p>논리적 설계를 바탕으로 물리적 설계를 하기 위해서 용어사전과 도메인 설계를 가장 먼저 진행하였다.</p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/169ac5c5-79ff-450e-9a50-91632952fc98/image.png" alt=""></p>
<p>용어사전은 정해진 규칙에 따라 더 분리할 수 없는 최소단위의 단수명사를 가능한 사용하려고 노력하였고 약어를 활용했다.</p>
<p>위의 사진은 <strong>용어사전</strong>의 일부이다.</p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/b4764116-611d-4532-b4d0-b7394edcd83c/image.png" alt=""></p>
<p>위의 사진은 <strong>도메인 기술서</strong>의 일부이고 좀 단순하지만 새로 알게된 점이 있었다. </p>
<p><strong>계좌번호</strong>나 <strong>전화번호</strong>같은 경우에 연산이 불필요하며 <code>-</code>와 같은 기호도 포함될 수 있고, 데이터의 손실 등의 오류를 초래할 수 있기에 Int 보단 문자열로 관리하는게 맞다는 점이었다. </p>
<p>이런 점 외에도 여러 점들을 고려해서 도메인을 설계할 수 있었다.</p>
<h2 id="물리적-설계">물리적 설계</h2>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/67bf4815-412a-4a3f-bbeb-11e032e7b914/image.png" alt=""></p>
<p>ERD에 용어사전과 도메인 기술서를 바탕으로 물리적 설계를 진행하였다.</p>
<h2 id="ddl-구축-및-샘플-데이터-삽입">DDL 구축 및 샘플 데이터 삽입</h2>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/b6f1dc2a-89cc-44a2-9354-39e63d424179/image.png" alt=""></p>
<p><strong>Toad Data Modeler</strong>을 통해 <strong>DDL</strong>을 구축하고 <strong>MYSQL WorkBench</strong>를 활용해 테이블을 생성하고 러프한 샘플 데이터를 삽입했다.</p>
<h2 id="샘플-데이터를-바탕으로-sql-시연">샘플 데이터를 바탕으로 SQL 시연</h2>
<p>각종 SQL 문을 실행하며 교수님과 학우들 앞에서 최종 보고서와 함께 시연했다.</p>
<p>대동제와 시험기간 두가지의 사례를 보여주기 위해 극단적인 샘플 데이터를 삽입한체로 시연했다.</p>
<h3 id="학교-축제기간-필요-인력-예측">학교 축제기간 필요 인력 예측</h3>
<p>작년 학교 축제때의 매출 변화 추이를 보며 올해에는 어떻게 인력배치를 해야할지 고민하는 예시이다.</p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/0e2ef642-c9e5-4820-beb0-dc4dbe6669df/image.png" alt=""></p>
<p>올해 행사정보에서 대학축제가 있음을 확인하고</p>
<p>작년 대동제와 평소일의 데이터를 비교해봐야겠다라고 생각을 한다.</p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/8b38ac75-7f73-49fb-ada7-a9616bb23e52/image.png" alt=""></p>
<p>축제때 매출이 많이 나왔음을 확인했지만 더 알아보고자 시간대별 매출을 알아본다.</p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/6fa8b8ff-5f67-449e-8b07-889c04913c55/image.png" alt=""></p>
<p>축제때가 평소보다 매출은 많지만 학생들이 축제가 끝나고 2차로 많이 오는건지 22시 이전에는 오히려 평소보다 손님이 없다.</p>
<blockquote>
<p>올해 대동제에는 22시 전엔 인원 1명만 배치해도 되겠다는 결론을 내린다.</p>
</blockquote>
<h3 id="기말고사-시험기간-필요-인력-예측">기말고사 시험기간 필요 인력 예측</h3>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/e058f563-1e5f-494e-b28f-8810aeeecbca/image.png" alt=""></p>
<p>시험 기간과 평소일의 매출을 비교해보니 여러가지 정보를 알 수 있었다.</p>
<ol>
<li>시험기간엔 손님이 많이 안오구나 </li>
<li>시험기간 직후에는 손님이 많이 오는구나</li>
<li>시험기간이어도 금요일이면 장사가 잘되는구나</li>
</ol>
<p>시험기간의 손님 유형을 보려고한다.</p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/d993a476-120e-48d5-b04f-2a3f70a677fe/image.png" alt=""></p>
<p>시험기간엔 학생보단 일반인이 더 많다는 특징을 찾아내어서 방문 시간대까지 한번 보려고한다.</p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/017d32f1-f4c8-4407-b124-a4efe1801137/image.png" alt=""></p>
<blockquote>
<p>일반인들은 학생들보다 일찍오는 편이어서 시험기간엔 이른시간엔 많이 배치하고 늦은 시간에는 최소인력만 투입시켜도 되겠다는 결론을 내린다.</p>
</blockquote>
<h3 id="앞선-정보를-고려한-인력-배치">앞선 정보를 고려한 인력 배치</h3>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/4ef8e316-274a-4a4c-b752-be633335606c/image.png" alt=""></p>
<p>Insert문을 통해 기존의 직원 정보를 활용해 배치한다.</p>
<p><img src="https://velog.velcdn.com/images/kt_gml/post/d4fc0972-9228-4b05-bc9a-c3b73ad2609f/image.png" alt=""></p>
<p>이와 같이 잘 배치되었음을 알 수 있다.
<br><br></p>
<h2 id="결론-및-느낀점">결론 및 느낀점</h2>
<p>이처럼 인력 필요량을 예측할 수 있도록 여러 데이터를 수집 저장 분석할 수 있는 테이블과 테이블간의 관계를 설계하고 실제로 데이터들의 상관관계를 통해 인력 필요량을 정해 배치까지 해볼 수 있었다.</p>
<p>데이터베이스의 여러 개념들을 익히고 논리적 설계와 물리적 설계를 거쳐 SQL까지 다뤄볼 수 있는 좋은 경험이었다.</p>
<p>1학년을 보내면서 대학이라는 곳이 단순히 강의를 듣는 곳인줄 알았는데 이렇게 직접 인터뷰도 해보고 실무에서 사용할 수 있는 데이터베이스를 설계해보니 신기했고 4.41이라는 좋은 학점으로 학기를 마무리해 뿌듯하다.</p>
<p>프로젝트를 진행하며 필요한 부분들을 스스로 더 공부해본 경험, 그리고 정말 많은 시간을 할애하며 최종 보고서를 작성하고 발표 시연하고, 팀장으로써 팀원들을 이끌고 리드해본 값진 경험은 당시엔 힘들었지만 나를 더 성장시킨 것 같다. </p>
<p>또 앞으로 어떤식으로 학습해야할지도 감 잡을 수 있었던 것 같다.</p>
<p>이외에도 CS 지식을 다루는 다른 과목들도 벨로그에 작성하며 복습하고 되돌아보는 시간을 가지려고 한다.</p>
]]></description>
        </item>
    </channel>
</rss>