<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Federico-15.log</title>
        <link>https://velog.io/</link>
        <description>한 방 있는, 묵직한 개발자</description>
        <lastBuildDate>Wed, 25 Jun 2025 05:21:18 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Federico-15.log</title>
            <url>https://velog.velcdn.com/images/coding_goat/profile/32f9609f-cc8e-4b77-94e2-09edb57ad9ab/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Federico-15.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/coding_goat" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[AWS] EC2를 이용하여 배포하기]]></title>
            <link>https://velog.io/@coding_goat/AWS-EC2%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@coding_goat/AWS-EC2%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 25 Jun 2025 05:21:18 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요! 오랜만의 포스팅입니다. 오늘은 AWS에서 제공하는 가장 기본적인 서비스인 EC2에 대해서 다룰 예정입니다.</p>
<h3 id="1-ec2elastic-compute-cloud-란">1. EC2(Elastic Compute Cloud) 란?</h3>
<p>한 줄 요약으로 설명하자면,</p>
<blockquote>
<p>컴퓨터를 빌려서 원격으로 접속해 사용하는 서비스이다.</p>
</blockquote>
<p>EC2를 쉽게 얘기하면 하나의 컴퓨터를 의미합니다. EC2는 AWS에서 제공하는 가상 서버 서비스로, 인터넷 상에 내 서버를 몇 분 만에 띄울 수 있게 해줍니다.
이 말로는 감이 안잡힐텐데, 차근차근 알아가봅시다!</p>
<hr>
<h3 id="2-ec2를-사용하는-이유는">2. EC2를 사용하는 이유는?</h3>
<p>서버를 배포하기 위해서는 컴퓨터가 필요합니다. 내가 가진 컴퓨터에서 서버를 배포해 다른 사용자들이 인터넷을 통해 접근할 수 있게 만들 수 있습니다. 하지만 자신의 컴퓨터로 서버를 배포하면 24시간동안 컴퓨터를 켜놔야 할 뿐 아니라, 보안적으로도 위험할 수 있습니다.</p>
<p>이러한 불편함 때문에 자신이 가지고 있는 컴퓨터를 사용하지 않고 AWS EC2라는 컴퓨터를 빌려서 사용하는 것입니다. 이 외에도 AWS EC2는 여러 부가기능들(로깅, 오토스케일링, 로드밸런싱 등)을 많이 가지고 있습니다.</p>
<hr>
<h3 id="3-실습">3. 실습</h3>
<h4 id="3-1--리전region-선택하기">3-1 . 리전(Region) 선택하기</h4>
<p>AWS EC2 서비스로 들어가서 리전(Region)을 선택해야합니다.</p>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/10822f08-04df-4197-bd86-b5240143218f/image.png" alt=""></p>
<hr>
<p><strong>리전이란?</strong></p>
<blockquote>
<p>리전(Region)이란 인프라를 지리적으로 나누어 배포한 각각의 데이터 센터를 의미합니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/14516f26-9e53-4858-9972-df8526772d74/image.png" alt=""></p>
<p>우린 EC2가 컴퓨터를 빌려서 원격으로 접속해 사용하는 서비스라는 걸 알고 있죠? 여기서 EC2를 통해 빌려서 쓸 수 있는 컴퓨터들이 전 세계적으로 다양하게 분포해있습니다. 이렇게 컴퓨터들이 위치한 위치를 보고 AWS에서는 리전(Region)이라고 합니다! </p>
<p>이러한 리전을 어떤 기준으로 선택하는지에 대해서 알아보겠습니다. 사용자의 위치와 애플리케이션을 실행시키고 있는 컴퓨터와 위치가 멀면 멀수록 속도가 느려집니다. </p>
<p>애플리케이션의 주된 사용자들의 위치와 <strong>지리적으로 가까운 리전(Region)을 선택하는 것이 유리</strong>합니다. 따라서 한국유저들이 주로 사용하는 서비스를 만들게 되면 리전(Region)을 아시아 태평양(서울)로 선택하면 됩니다.</p>
<hr>
<h4 id="3-2-ec2-셋팅하기---기본-설정">3-2. EC2 셋팅하기 - 기본 설정</h4>
<p>이제 EC2 인스턴스 생성을 본격적으로 해보겠습니다.</p>
<hr>
<h4 id="3-3-이름-및-태그">3-3. 이름 및 태그</h4>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/03f3d3d8-dc3f-477e-acfa-c9a120e0e88f/image.png" alt=""></p>
<p>이름 및 태그는 EC2의 이름을 설정하는 곳입니다. 이름을 지을 때는 이 컴퓨터가 어떤 역할을 하는지 알아볼 수 있어야합니다. 저는 my-applicaiton으로 작성했습니다!</p>
<hr>
<h4 id="3-4-애플리케이션-및-os-이미지">3-4. 애플리케이션 및 OS 이미지</h4>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/373c658f-ec55-4fc5-aba0-1c4410f30930/image.png" alt=""></p>
<p>OS를 선택하는 단계입니다. Windows나 Mac OS를 선택하지 않은 이유는 용량도 많이 차지하고, 성능도 많이 잡아먹기에 선택하지 않았습니다. 서버를 배포할 컴퓨터의 OS는 훨씬 가벼운 Ubuntu를 많이 사용합니다.</p>
<hr>
<h4 id="3-5-인스턴스-유형">3-5. 인스턴스 유형</h4>
<p>인스턴스란, AWS EC2에서 빌리는 컴퓨터 1대를 의미합니다. 인스턴스 유형은 컴퓨터 사양을 의미합니다. 컴퓨터 사양이 좋으면 좋을수록 많은 수의 요청을 처리할 수 있고, 무거운 서버나 프로그램을 돌릴 수 있겠죠? 저희는 프리티어에 해당하는 t2.micro를 사용하여 실습하겠습니다.</p>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/966d9da2-4f35-4b26-8758-119dcd11e229/image.png" alt=""></p>
<hr>
<h4 id="3-6-키-페어-로그인">3-6. 키 페어 (로그인)</h4>
<p>키 페어(Key Pair)는 무슨 뜻일까요? EC2 컴퓨터에 접근할 때 사용하는 비밀번호라고 생각하면 됩니다. 말 그대로 열쇠(Key, 키)의 역할을 합니다.</p>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/23c9cdf4-777e-47b6-8445-a4c7012b127f/image.png" alt=""></p>
<p>키페어 이름은 어떤 EC2에 접근하기 위한 키 페어였는지 알아볼 수 있게 지정하면 좋습니다. 전 practice로 지정해두겠습니다.</p>
<p>키페어 생성을 누르면,</p>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/942d1391-81b3-4526-9ec5-90e3c3824589/image.png" alt=""></p>
<p>이런식으로 파일이 하나 다운받아지는데 이 파일은 잃어버리면 안됩니다! 참고로, 실습에서는 키 페어를 활용해서 EC2에 접근하지 않고, 다른 방법으로 접근할 예정입니다.</p>
<hr>
<h4 id="3-7-보안그룹-설정">3-7. 보안그룹 설정</h4>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/d7311eb9-cfab-41aa-ab21-5a7c1beabafe/image.png" alt=""></p>
<p>네트워크 설정 칸을 보면 <strong>VPC</strong>와 <strong>Security Groups(보안 그룹)</strong>가 보입니다. 여기서 VPC라는 개념은 나중에 다뤄보겠습니다. </p>
<p>하지만 <strong>Security Groups(보안 그룹)</strong>은 서버를 배포할 때 중요한 개념이므로 자세히 알아보겠습니다.</p>
<hr>
<h4 id="보안-그룹-security-group이란">보안 그룹 (Security Group)이란?</h4>
<p>보안 그룹이란 AWS 클라우드에서의 네트워크 보안을 의미합니다. 
<img src="https://velog.velcdn.com/images/coding_goat/post/d9c687e6-1a5a-4cf9-a76b-f692eefe4246/image.png" alt=""></p>
<p>일부 사용자가 EC2 인스턴스에 접근(액세스) 하려고 한다면 EC2 인스턴스 주위에 방화벽 역할을 할 보안그룹을 만들고 보안 그룹에 규칙을 지정합니다. 이 보안 규칙에는 <strong>인바운드 트래픽(외부에서 EC2 인스턴스로 보내는 트래픽)</strong>에서 어떤 트래픽만 허용할지 설정할 수 있고, <strong>아웃바운드 트래픽(EC2 인스턴스에서 외부로 나가는 트래픽)</strong>에서 어떤 트래픽만 허용할 지 설정할 수 있습니다.</p>
<p>그렇다면 EC2 인스턴스를 생성할 때 어떻게 보안그룹을 설정해야하는지 알아봅시다.</p>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/ef9eabf1-9d07-4e71-a01f-ce508729855b/image.png" alt=""></p>
<p>외부에서 EC2로 접근할 포트는 22번포트와 80번 포트라고 생각하여 이 2가지에 대해서 인바운드 보안 그룹 규칙을 추가했습니다. 22번 포트는 우리가 EC2에 원격접속할 때 사용하는 포트이고, 80번 포트에는 백엔드 서버를 띄울 것이기 때문입니다. 소스 유형은 어떤 IP에서든 전부 접근할 수 있게 만들기 위해 위치 무관으로 설정하였습니다.</p>
<hr>
<h4 id="3-8-스토리지-구성">3-8. 스토리지 구성</h4>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/bbcbcc87-1866-42bb-b5df-5f1eabb3e9df/image.png" alt=""></p>
<p>스토리지란 EC2의 저장공간을 뜻합니다. 이러한 저장공간을 EBS(Elastic Block Storage)라고 부릅니다. 이것을 좀 더 포괄적인 용어로 스토리지, 볼륨이라고 부릅니다. </p>
<p>여러 종류의 스토리지가 있지만, 가성비가 좋은 gp3을 선택하겠습니다. 프리티어에서 30GiB까지 무료로 제공하기에 용량 또한, 30GiB로 설정하겠습니다.</p>
<hr>
<h4 id="3-9-ec2-접속하기">3-9. EC2 접속하기</h4>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/05eff0a1-a5ba-42e5-9c99-c329f2c5f7b3/image.png" alt=""></p>
<p>이렇게 인스턴스 시작을 누르면,</p>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/11cc57c6-acad-41b4-a262-8d5757b3f707/image.png" alt=""></p>
<p>EC2 인스턴스가 생성된 것을 볼 수 있습니다. 하지만, 이것이 끝이 아닙니다. EC2 인스턴스를 생성하면 IP를 할당받습니다. 하지만 이렇게 할당받은 IP는 임시적인 IP입니다. EC2 인스턴스를 잠깐 중지시켰다가 다시 실행해보겠습니다. </p>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/4f192e94-8ce0-4f3b-a143-b5e696ad4ae1/image.png" alt=""></p>
<hr>
<h4 id="3-10-탄력적-ip">3-10. 탄력적 IP</h4>
<p>이런식으로 IP가 변경된 것을 볼 수 있습니다. 다시 실행할 때 마다 IP가 바뀌면 굉장히 불편합니다. 따라서 중지시켰다가 다시 실행시켜도 바뀌지 않는 고정 IP를 할당받아야합니다. 그것이 <strong>탄력적 IP</strong>입니다.</p>
<p>탄력적 IP를 할당 하게 되면, 서버를 다시 실행하여도 IP가 바뀌지 않는 것을 볼 수 있습니다. 탄력적 IP를 생성한 뒤 ,</p>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/f9ce8d55-c49f-43d8-8381-04b1ac1b91c5/image.png" alt=""></p>
<p>이렇게 연결하면 끝입니다!</p>
<hr>
<h4 id="마무리">마무리</h4>
<p>이런식으로 EC2를 생성하고, 접속하는 것까지 완료하였습니다. 또한, 탄력적 IP를 생성하여 고정IP를 할당 받는 프로세스까지 해봤습니다. 다음 포스트 때는 EC2를 이용하여 배포하는 실습을 진행하겠습니다!! 다음 포스트 때 봐용~!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OpenSource] 오픈소스 기여 하기!!]]></title>
            <link>https://velog.io/@coding_goat/OpenSource-%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%EA%B8%B0%EC%97%AC-%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@coding_goat/OpenSource-%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%EA%B8%B0%EC%97%AC-%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 21 Feb 2025 15:34:40 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요! 이번 글에서는 제가 처음으로 오픈소스 컨트리뷰션을 하면서 경험했던 과정을 공유하려고 합니다.
오픈소스에 기여하고 싶은데 &quot;어디서부터 시작해야 할지?&quot;, &quot;어떤 프로젝트를 선택해야 할지?&quot; 고민되셨다면, 이 글을 참고하시면 좋을 것 같습니다! 😊</p>
<hr>
<h3 id="1-오픈소스-프로젝트--이슈-찾기">1. 오픈소스 프로젝트 &amp; 이슈 찾기</h3>
<h4 id="🔍-good-first-issue-태그-활용하기">🔍 &quot;Good First Issue&quot; 태그 활용하기</h4>
<p>처음 오픈소스 컨트리뷰션을 할 때는 초보자도 쉽게 기여할 수 있는 이슈를 찾는 것이 중요합니다. 많은 오픈소스 프로젝트에서는 초보자를 위해 &quot;Good First Issue&quot; 또는 &quot;Help Wanted&quot; 같은 태그를 붙여둡니다.</p>
<p> 이런 태그가 달린 이슈들은 비교적 난이도가 낮고, 프로젝트 기여 방식에 익숙해질 수 있도록 구성되어 있습니다.</p>
<p> 이렇게 깃 검색창에 label:&quot;good first issue&quot; 라고 검색하면,</p>
<p> <img src="https://velog.velcdn.com/images/coding_goat/post/7fc301da-499d-4d2d-8727-a3affdf8f73f/image.png" alt=""></p>
<hr>
<p>이렇게 수많은 &quot;Good first issue&quot; 라벨을 가진 issue 들을 볼 수 있습니다.</p>
<p> <img src="https://velog.velcdn.com/images/coding_goat/post/97f7c4be-6cae-417e-a47e-6faf52a0d7dd/image.png" alt=""></p>
<p>저는, 수많은 이슈들 중 <code>elimu-ai/analytics</code> 프로젝트에서 <code>&quot;Upgrade from Java 11 to 17&quot;</code> 이슈를 발견하였고, 해당 이슈를 해결하기 위해 어떤 작업이 필요한지 확인했습니다. 이 오픈소스에 기여하는 과정을 적어보겠습니다.</p>
<hr>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/e47ea540-4076-4821-a518-3239ffc11af6/image.png" alt="코멘트"></p>
<p>이런식으로, 코멘트를 남겼고, issue가 뭔지 구체화해달라고 요청을 하며, 이 issue를 저가 해결해도 되는지 물어봤습니다.</p>
<hr>
<p>감사하게도, 저에게 issue를 할당해주셨고, 전 작업을 진행할 수 있게 되었습니다.</p>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/388bb05f-8e1c-45fe-b02c-d47aefdd7eac/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/a9e06996-f085-4e16-b5cd-1c2687120d74/image.png" alt=""></p>
<hr>
<h3 id="2-fork부터-pr까지">2. Fork부터 PR까지</h3>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/7a653be0-2f45-4959-a2a7-14089d8a6dd1/image.png" alt=""></p>
<h4 id="2-1-fork--clone-내-저장소로-가져오기">2-1 Fork &amp; Clone (내 저장소로 가져오기)</h4>
<p>오픈소스 프로젝트에 직접 변경을 가하면 원본 코드가 망가질 수도 있기 때문에,
먼저 Fork(포크) 를 해서 내 GitHub 계정으로 복사한 후, 내 컴퓨터에 Clone(복제) 해야 합니다.</p>
<p>✔ Fork란?</p>
<blockquote>
<p>Fork는 원본 프로젝트를 내 GitHub 계정에 복사하는 과정입니다.
이렇게 하면 원본 프로젝트를 보호하면서 자유롭게 수정하고 테스트할 수 있는 공간이 생깁니다!</p>
</blockquote>
<p>✔ Clone이란?</p>
<blockquote>
<p>Fork한 프로젝트를 내 컴퓨터에 다운로드하여 직접 수정할 수 있도록 하는 과정입니다.</p>
</blockquote>
<hr>
<h4 id="2-2-새로운-브랜치-생성-및-코드-수정-요구사항-반영">2-2. 새로운 브랜치 생성 및 코드 수정 (요구사항 반영)</h4>
<p>그 이후, main 브랜치에서 직접 작업하는 것이 아니라, 새로운 feature 브랜치를 만들어 진행하였습니다.</p>
<p>그 뒤, 이슈에서 요구한 내용을 반영하여 코드 수정하였습니다.</p>
<blockquote>
</blockquote>
<ul>
<li>Java 11 → Java 17 업그레이드</li>
<li>build.gradle에서 Java 버전 업데이트</li>
<li>GitHub Actions에서 Java 17 지원하도록 수정</li>
<li>Model 라이브러리 버전 최신으로 변경</li>
</ul>
<hr>
<h4 id="2-3-빌드-및-테스트-진행">2-3. 빌드 및 테스트 진행</h4>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/aed1712c-f8f8-4197-ba90-78910a5ed337/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/a2524b88-110c-4d6f-a7c3-02d55255dff4/image.png" alt=""></p>
<p>빌드 및 테스트가 정상적으로 진행되었습니다.
특히 <strong>Java 버전(17.0.10)과 Gradle 버전(8.7)</strong>이 제대로 설정되었음을 보여주고 있고, Gradle 실행도 정상적으로 되고 있는 모습이 확인됩니다.</p>
<hr>
<h4 id="2-4-pull-request-pr-생성">2-4. Pull Request (PR) 생성</h4>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/ea14c396-8b5c-4b1a-8b21-177423f82824/image.png" alt=""></p>
<p>이렇게 PR을 요청하였습니다. </p>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/fd414758-8cea-41e9-b40e-85198cae31e8/image.png" alt=""></p>
<p>이 후, 코드 리뷰어가 PR을 검토한 후, 추가적인 변경 사항을 요청한 상태입니다.</p>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/cc4d027f-6e10-49a5-b667-b7ece37bf790/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/ff62000a-4888-4b91-836b-e967215a1867/image.png" alt=""></p>
<p>추가적인 요구 사항을 해결한 뒤, PR 승인을 받았습니다. 마지막으로 merge 를 한 뒤, 저는 이 오픈소스 프로젝트의 contributor가 되었습니다! ✌️😊</p>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/680882b5-463b-4ee5-811c-9453c054255d/image.png" alt=""></p>
<hr>
<h3 id="결론">결론</h3>
<p>이렇게 저는 오늘 오픈소스 컨트리뷰터가 되어봤습니다. 이번 기회를 통해 단순히 코드만 기여하는 것이 아니라, 프로젝트의 구조를 파악하고 커뮤니티와 소통하는 것이 얼마나 중요한지 배웠습니다. 앞으로도 지속적으로 오픈소스 프로젝트에 기여하면서 더 성장할 수 있는 개발자가 되고 싶습니다. 이 블로그 포스팅을 본 여러분도 오픈소스 컨트리뷰터가 한 번 되보시길 바라겠습니다!!</p>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/eaaefbd2-4f55-4677-9293-b4b8a74f707a/image.png" alt=""></p>
<p>(스타 수 10000개 될 때까지 화이팅!!!!!!)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OpenSource] 오픈소스 컨트리뷰션이란?]]></title>
            <link>https://velog.io/@coding_goat/OpenSource-%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%EC%BB%A8%ED%8A%B8%EB%A6%AC%EB%B7%B0%EC%85%98%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@coding_goat/OpenSource-%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%EC%BB%A8%ED%8A%B8%EB%A6%AC%EB%B7%B0%EC%85%98%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Fri, 21 Feb 2025 08:48:38 GMT</pubDate>
            <description><![CDATA[<h3 id="서론">서론</h3>
<p>안녕하세요!! 새로운 시리즈인 [OpenSource] 라는 시리즈로 찾아오게 되었습니다! 이 시리즈는 앞으로 &#39;<strong>오픈 소스 컨트리뷰션</strong>&#39;을 하며 경험을 공유하고, 기록하며 회고할 시리즈입니다!! </p>
<hr>
<h3 id="1-오픈소스oepnsource란">1. 오픈소스(OepnSource)란?</h3>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/90b8b62b-73cb-4e64-a897-40279a648d35/image.png" alt="">
<code>오픈소스 컨트리뷰션(오픈소스 기여)</code> 를 하려면 먼저 <code>오픈소스(OpenSource)</code>가 뭔지에 대해서 알아야겠죠? 오픈소스가 무엇일까요?<br>오픈소스(Open Source)는 말 그대로 <strong>&quot;소스 코드가 공개된 소프트웨어&quot;</strong>를 의미합니다. 즉, 누구나 소스 코드를 보고, 수정하고, 개선할 수 있는 자유로운 소프트웨어를 뜻합니다!</p>
<p>또한, AWS에서 정의한 오픈소스 개념을 참고하면, 오픈소스는 단순한 코드 공개를 넘어 개방성과 협업을 중심으로 한 개발 문화를 의미하기도 합니다.
<a href="https://aws.amazon.com/ko/what-is/open-source/">https://aws.amazon.com/ko/what-is/open-source/</a> (내용 참고)</p>
<p><strong>오픈소스의 특징</strong></p>
<blockquote>
</blockquote>
<p>✔ 소스 코드 공개 → 누구나 열람 가능
✔ 수정 및 배포 가능 → 자유롭게 수정하고 다시 배포할 수 있음
✔ 커뮤니티 중심 개발 → 여러 개발자들이 협력하여 발전시킴
✔ 라이선스 적용 → 다양한 오픈소스 라이선스를 따름 (MIT, Apache, GPL 등)</p>
<p><strong>오픈소스로 만들어진 대표적인 프로젝트들</strong></p>
<blockquote>
</blockquote>
<p>✔ 운영체제 → Linux, Android
✔ 클라우드 &amp; 컨테이너 → Kubernetes, Docker, Terraform
✔ 프레임워크 &amp; 라이브러리 → Spring Boot, React, TensorFlow
✔ 데이터베이스 → MySQL, PostgreSQL</p>
<p>이처럼 우리가 개발하면서 자주 사용하는 기술 중 대부분이 오픈소스 기반입니다.  </p>
<hr>
<h3 id="2-오픈소스-컨트리뷰션opensource-contribution이란">2. 오픈소스 컨트리뷰션(OpenSource contribution)이란?</h3>
<blockquote>
<p>오픈소스에 대해서는 이제 뭔지 알게 되었고, 그럼 오픈소스 컨트리뷰션은 대체 뭐야? </p>
</blockquote>
<p><code>오픈소스</code>는 단순히 무료로 가져다 쓰는 것만이 아니라, 누구나 직접 기여할 수 있는 열린 프로젝트라고 말씀드렸죠? <code>오픈소스 컨트리뷰션</code>은 단순히 소프트웨어를 사용하는 것에서 벗어나, 직접 프로젝트에 기여하는 활동을 의미합니다. 단순히 개발자가 아니라, 기여할 수 있는 방법이 다양해서 누구든지 참여할 수 있습니다.</p>
<blockquote>
<ol>
<li><strong>버그 수정</strong> 🐛
오픈소스 소프트웨어를 사용하다 보면 예상치 못한 오류나 버그를 발견할 수도 있습니다. 이런 경우, 단순히 오류를 보고하는 것에서 끝나는 것이 아니라, 직접 원인을 찾아 해결한 뒤, 수정된 코드를 공유하면 다른 사용자들에게도 큰 도움이 됩니다. 이를 통해 프로젝트가 더욱 안정적으로 개선될 수 있습니다.</li>
</ol>
</blockquote>
<blockquote>
<ol start="2">
<li><strong>**새로운 기능 추가</strong> ⚡
기존 프로젝트에 필요한 기능이 부족할 수도 있습니다. 예를 들어, 특정 API 기능이 없거나 UI에서 추가적인 편의 기능이 필요할 수도 있습니다. 이럴 때, 직접 기능을 개발해서 프로젝트에 반영하면 더 많은 사람이 편리하게 사용할 수 있도록 도울 수 있습니다.</li>
</ol>
</blockquote>
<blockquote>
<ol start="3">
<li><strong>문서화 기여</strong> 📖
오픈소스 프로젝트에서는 소프트웨어 자체뿐만 아니라 <strong>문서(Documentation)</strong>도 매우 중요합니다. 아무리 좋은 프로젝트라도 사용법이 명확하지 않으면 활용하기 어려울 수 있습니다. README 파일을 보강하거나, 가이드를 추가하고, 심지어 다국어 번역을 제공하는 것도 훌륭한 기여 방법입니다. 특히 개발 경험이 적은 사람도 기여할 수 있는 좋은 방식 중 하나입니다.</li>
</ol>
</blockquote>
<blockquote>
<ol start="4">
<li><strong>이슈 해결</strong> 🔍
많은 오픈소스 프로젝트는 GitHub의 이슈(issues) 기능을 통해 사용자들이 발견한 문제나 개선점을 공유할 수 있도록 하고 있습니다. 프로젝트를 살펴보다가 해결할 수 있는 이슈가 있다면, 직접 해결 방법을 찾아 제안하거나 코드를 수정하여 기여할 수 있습니다.</li>
</ol>
</blockquote>
<p>이처럼 오픈소스 컨트리뷰션은 단순한 참여가 아니라, 더 좋은 소프트웨어를 함께 만들어가는 협업의 과정입니다. 개발자뿐만 아니라 누구나 다양한 방식으로 기여할 수 있고, 이를 통해 성장할 수 있는 기회도 얻을 수 있습니다.</p>
<hr>
<p>다음 포스트에서는 오픈소스 컨트리뷰션을 어떻게 시작할 수 있는지에 대해 다뤄볼 예정입니다. </p>
<p>처음 오픈소스에 기여하려고 하면 &quot;어떤 프로젝트를 선택해야 할지?&quot;, &quot;어디서부터 시작해야 할지?&quot;, &quot;PR은 어떻게 보내야 하는지?&quot; 같은 고민이 생길 수밖에 없습니다. 저 역시 처음엔 막막했지만, 한 걸음씩 차근차근 따라가다 보니 누구나 기여할 수 있다는 것을 깨달았습니다!</p>
<p>특히 저같은 취준생이나 개발자로 성장하고 싶은 분들도 얼마든지 오픈소스에 참여할 수 있습니다.</p>
<p>다음 글에서는 <strong>&quot;오픈소스에 기여하는 방법&quot;</strong>을 단계별로 정리해보겠습니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 우선순위 큐(Priority Queue)]]></title>
            <link>https://velog.io/@coding_goat/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84-%ED%81%90Priority-Queue</link>
            <guid>https://velog.io/@coding_goat/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84-%ED%81%90Priority-Queue</guid>
            <pubDate>Thu, 20 Feb 2025 01:23:57 GMT</pubDate>
            <description><![CDATA[<h3 id="서론">서론</h3>
<p>코딩테스트를 준비할 때 스택과 큐를 공부했다면, 그 다음으로 공부해야 할 것은 <code>Priority Queue</code>입니다. 이번 포스트에서는 <code>Priority Queue</code>가 무엇인지, 간단한 구현 예제를 포스트하겠습니다.</p>
<hr>
<h3 id="1-priority-queue">1. Priority Queue</h3>
<p><code>Priority Queue</code>는 일반적인 <code>Queue</code>와 다르게 들어온 순서가 아닌 요소의 우선순위에 따라 요소를 처리하는 자료구조입니다. 우선순위 큐의 경우 힙 자료구조를 통해서 구현될 수 있습니다. 기본적으로 <strong>오름차순</strong>으로 정렬되지만, 우선순위를 객체로 <strong>직접 정의</strong>하여 우선순위를 설정하여 정렬할 수 있습니다. </p>
<hr>
<h3 id="1-1-priority-queue의-기본-연산">1-1. Priority Queue의 기본 연산</h3>
<ul>
<li><code>offer()</code> : 요소를 추가 (우선순위에 따라 정렬)</li>
<li><code>poll()</code> : 가장 우선순위가 높은 요소 제거 및 반환</li>
<li><code>peek()</code> : 가장 우선순위가 높은 요소 반환(제거 x)</li>
<li><code>isEmpty()</code> : 큐가 비어있는지 확인(true or false)</li>
<li><code>size()</code> : 큐에 들어 있는 요소의 개수 반환</li>
</ul>
<hr>
<h3 id="1-2-priority-queue-예시-코드-기본형--오름차순">1-2. Priority Queue 예시 코드 (기본형 : 오름차순)</h3>
<pre><code>import java.util.PriorityQueue;

public class PriorityQueueExample {
    public static void main(String[] args) {
        PriorityQueue&lt;Integer&gt; pq = new PriorityQueue&lt;&gt;(); // 기본: 오름차순

        pq.offer(30);
        pq.offer(10);
        pq.offer(20);
        pq.offer(50);
        pq.offer(40);

        while (!pq.isEmpty()) {
            System.out.println(pq.poll()); // 10, 20, 30, 40, 50 (작은 값부터)
        }
    }
}
</code></pre><p>위의 코드를 통해 Priority Queue는 기본적으로 오름차순으로 정렬되는 것을 볼 수 있습니다.</p>
<hr>
<h3 id="1-3-priority-queue-예제-내림차순">1-3. Priority Queue 예제 (내림차순)</h3>
<p>기본 동작과 다르게 <strong>큰 값이 먼저 나오도록 (내림차순) 우선순위를 설정</strong>할 수 있습니다.</p>
<pre><code>import java.util.PriorityQueue;
import java.util.Collections;

public class DescendingPriorityQueue {
    public static void main(String[] args) {
        PriorityQueue&lt;Integer&gt; pq = new PriorityQueue&lt;&gt;(Collections.reverseOrder()); // 내림차순 정렬

        pq.offer(30);
        pq.offer(10);
        pq.offer(50);
        pq.offer(20);

        while (!pq.isEmpty()) {
            System.out.println(pq.poll()); // 50 → 30 → 20 → 10 (내림차순)
        }
    }
}

</code></pre><hr>
<h3 id="1-4-priority-queue-우선순위-설정">1-4. Priority Queue (우선순위 설정)</h3>
<p>Priority Queue의 꽃인 우선순위를 설정하고, 우선순위에 따라 처리하는 Priority Queue를 구현하는 예제를 적어보겠습니다.</p>
<p>아래 예제는 국어 점수가 높은 순으로 정렬하며, 국어 점수가 같으면 영어점수가 높은 순으로 정렬하는 예제입니다.</p>
<pre><code>
import java.util.PriorityQueue;
import java.util.Comparator;

class Student {
    String name;
    int koreanScore;
    int englishScore;

    public Student(String name, int koreanScore, int englishScore) {
        this.name = name;
        this.koreanScore = koreanScore;
        this.englishScore = englishScore;
    }

    @Override
    public String toString() {
        return name + &quot; (국어: &quot; + koreanScore + &quot;, 영어: &quot; + englishScore + &quot;)&quot;;
    }
}

public class StudentPriorityQueue {
    public static void main(String[] args) {
        // Comparator: 국어 점수 내림차순 -&gt; 국어 점수 같으면 영어 점수 내림차순
        PriorityQueue&lt;Student&gt; pq = new PriorityQueue&lt;&gt;((a, b) -&gt; {
            if (b.koreanScore != a.koreanScore) {
                return b.koreanScore - a.koreanScore; // 국어 점수 높은 순
            }
            return b.englishScore - a.englishScore; // 국어 점수 같으면 영어 점수 높은 순
        });

        pq.offer(new Student(&quot;김철수&quot;, 90, 80));
        pq.offer(new Student(&quot;이영희&quot;, 95, 85));
        pq.offer(new Student(&quot;박지훈&quot;, 90, 85));
        pq.offer(new Student(&quot;정수민&quot;, 80, 90));

        while (!pq.isEmpty()) {
            System.out.println(pq.poll()); 
        }
    }
}</code></pre><p>실행결과</p>
<blockquote>
</blockquote>
<p>이영희 (국어: 95, 영어: 85)
김철수 (국어: 90, 영어: 80)
박지훈 (국어: 90, 영어: 85)
정수민 (국어: 80, 영어: 90)</p>
<hr>
<h3 id="결론">결론</h3>
<p>우선순위 큐는 특정 기준에 따라 요소를 정렬하여 처리해야 할 때 매우 유용한 자료구조입니다. 이번 포스트에서는 국어 점수와 영어 점수를 기준으로 학생을 정렬하는 우선순위 큐를 구현해 보았습니다.</p>
<p>우선순위 큐의 핵심은 Comparator를 활용하여 정렬 기준을 자유롭게 설정할 수 있다는 점입니다. 이를 통해 단순한 숫자 정렬뿐만 아니라 객체의 특정 필드를 기반으로 동적 우선순위를 부여하는 활용도 가능합니다.</p>
<p>이제 여러분도 <code>PriorityQueue</code>를 활용하는 문제를 박살낼 수 있습니다!! </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JSP를 활용한 간단한 회원관리 시스템 구현]]></title>
            <link>https://velog.io/@coding_goat/JSP%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EA%B0%84%EB%8B%A8%ED%95%9C-%ED%9A%8C%EC%9B%90%EA%B4%80%EB%A6%AC-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@coding_goat/JSP%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EA%B0%84%EB%8B%A8%ED%95%9C-%ED%9A%8C%EC%9B%90%EA%B4%80%EB%A6%AC-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Tue, 04 Feb 2025 05:57:25 GMT</pubDate>
            <description><![CDATA[<h3 id="서론">서론</h3>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/ee2e7b9f-b71c-4366-b191-2c399c217706/image.png" alt=""></p>
<p>안녕하세요! 이전 포스트에서 서블릿을 활용하여 간단한 회원 관리 시스템을 구현해보았고, 서블릿의 한계에 대해서 알아보았습니다. 서블릿을 이용하게 되면 웹 프로그래밍이 가능해지지만, 화면 인터페이스 구현에 너무 많은 코드가 필요해지는 등의 단점이 있었죠? </p>
<blockquote>
</blockquote>
<p>기억이 안나신다면 ? <a href="https://velog.io/@coding_goat/%EC%84%9C%EB%B8%94%EB%A6%BFServlet%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EA%B0%84%EB%8B%A8%ED%95%9C-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%ED%98%84">이전포스트</a> 보고 오시는것을 추천합니다. ㅎㅎ</p>
<p>이러한 단점을 보완하기 위해 나온것이 템플릿 엔진이었고, 템플릿 엔진 중에 하나가 JSP 입니다. 오늘 포스트에서는 이전 포스트에서 <strong>서블릿</strong>으로 진행했던 간단한 회원가입, 조회, 목록보기 작업을 <strong>JSP</strong>로 진행해보겠습니다. </p>
<hr>
<h3 id="1-jsp-라이브러리-추가">1. JSP 라이브러리 추가</h3>
<p>JSP를 사용하려면 먼저 다음 라이브러리를 <code>build.gradle</code>에 추가해야 합니다.</p>
<pre><code>// JSP 의존성 추가 (스프링부트 3.0 미만)
 implementation &#39;org.apache.tomcat.embed:tomcat-embed-jasper&#39;
 implementation &#39;javax.servlet:jstl&#39;</code></pre><p> ( 저는 스프링부트 3.0 이상이기에 밑에 코드를 추가했습니다. )</p>
<pre><code> // JSP 의존성 추가 (스프링부트 3.0 이상)
 implementation &#39;org.apache.tomcat.embed:tomcat-embed-jasper&#39;
 implementation &#39;jakarta.servlet:jakarta.servlet-api&#39; 
implementation &#39;jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api&#39; 
implementation &#39;org.glassfish.web:jakarta.servlet.jsp.jstl&#39; </code></pre><p><img src="https://velog.velcdn.com/images/coding_goat/post/18bd0bc2-3b27-4bc3-ab12-e6c914ed0d06/image.png" alt=""></p>
<p>라이브러리 추가 이후에 Gradle을 refresh해주시는 것 잊지 말아주시고요! 
(코끼리 클릭!!)</p>
<hr>
<h3 id="2-회원-등록-폼-jsp">2. 회원 등록 폼 JSP</h3>
<p><code>main/webapp/jsp/members/new-form.jsp</code></p>
<pre><code> &lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; language=&quot;java&quot; %&gt;
 &lt;html&gt;
 &lt;head&gt;
    &lt;title&gt;Title&lt;/title&gt;
 &lt;/head&gt;
 &lt;body&gt;
 &lt;form action=&quot;/jsp/members/save.jsp&quot; method=&quot;post&quot;&gt;
    username: &lt;input type=&quot;text&quot; name=&quot;username&quot; /&gt;
    age:      &lt;input type=&quot;text&quot; name=&quot;age&quot; /&gt;
    &lt;button type=&quot;submit&quot;&gt;전송&lt;/button&gt;
 &lt;/form&gt;
 &lt;/body&gt;
 &lt;/html&gt;</code></pre><hr>
<p><strong>코드 설명</strong></p>
<ul>
<li><code>&lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; language=&quot;java&quot; %&gt;</code></li>
<li><blockquote>
<p>JSP문서라는 뜻입니다. JSP 문서는 이렇게 시작합니다.</p>
</blockquote>
</li>
</ul>
<hr>
<p><strong>실행결과</strong>
<img src="https://velog.velcdn.com/images/coding_goat/post/eacc5d55-4cda-49df-95ee-fb8a5568ed5e/image.png" alt=""></p>
<hr>
<h3 id="3-회원-저장-jsp">3. 회원 저장 JSP</h3>
<p><code>main/webapp/jsp/members/new-form.jsp</code></p>
<pre><code>&lt;%@ page import=&quot;hello.servlet.domain.member.MemberRepository&quot; %&gt;
&lt;%@ page import=&quot;hello.servlet.domain.member.Member&quot; %&gt;
&lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; language=&quot;java&quot; %&gt;
&lt;%
    MemberRepository memberRepository = MemberRepository.getInstance();
    System.out.println(&quot;save.jsp&quot;);
    String username = request.getParameter(&quot;username&quot;);
    int age = Integer.parseInt(request.getParameter(&quot;age&quot;));
    Member member = new Member(username, age);
    System.out.println(&quot;member = &quot; + member);
    memberRepository.save(member);
%&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Title&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
성공
&lt;ul&gt;
    &lt;li&gt;id=&lt;%=member.getId()%&gt;&lt;/li&gt;
    &lt;li&gt;username=&lt;%=member.getUsername()%&gt;&lt;/li&gt;
    &lt;li&gt;age=&lt;%=member.getAge()%&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre><hr>
<p>** 코드 설명 **</p>
<ul>
<li><p><code>&lt;%@ page import=&quot;hello.servlet.domain.member.MemberRepository&quot; %&gt;</code></p>
</li>
<li><blockquote>
<p>자바의 import 문과 같습니다. </p>
</blockquote>
<p><code>import hello.servlet.domain.member.MemberRepository</code>
   자바로 표현하면 이렇게 되겠죠?</p>
</li>
<li><p><code>&lt;% ~~ %&gt;</code>
이 부분에는 자바 코드를 <strong>입력</strong>할 수 있습니다.</p>
</li>
<li><p><code>&lt;%= ~~ %&gt;</code>
이 부분에는 자바 코드를 <strong>출력</strong>할 수 있습니다.</p>
</li>
</ul>
<hr>
<p>회원 저장 JSP를 보면, 회원 저장 서블릿 코드와 같습니다. 다른 점이 있다면, HTML을 중심으로 하고, 자바 코드를 부분부분 입력했습니다. 
<code>&lt;% ~ %&gt;</code>를 사용해서 HTML 중간에 자바 코드를 출력하고 있습니다.</p>
<hr>
<h3 id="4-회원-목록-jsp">4. 회원 목록 JSP</h3>
<p><code>main/webapp/jsp/members.jsp</code></p>
<pre><code>&lt;%@ page import=&quot;java.util.List&quot; %&gt;
&lt;%@ page import=&quot;hello.servlet.domain.member.MemberRepository&quot; %&gt;
&lt;%@ page import=&quot;hello.servlet.domain.member.Member&quot; %&gt;
&lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; language=&quot;java&quot; %&gt;
&lt;%
    MemberRepository memberRepository = MemberRepository.getInstance();
    List&lt;Member&gt; members = memberRepository.findAll();
%&gt;

&lt;html&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;title&gt;Title&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;a href=&quot;/index.html&quot;&gt;메인&lt;/a&gt;
&lt;table&gt;
    &lt;thead&gt;
    &lt;tr&gt;
        &lt;th&gt;id&lt;/th&gt;
        &lt;th&gt;username&lt;/th&gt;
        &lt;th&gt;age&lt;/th&gt;
    &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
    &lt;%-- JSP 스크립틀릿에서 반복문을 사용 --%&gt;
    &lt;%
        for (Member member : members) {
    %&gt;
    &lt;tr&gt;
        &lt;td&gt;&lt;%= member.getId() %&gt;&lt;/td&gt;
        &lt;td&gt;&lt;%= member.getUsername() %&gt;&lt;/td&gt;
        &lt;td&gt;&lt;%= member.getAge() %&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;%
        }
    %&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p><strong>코드 설명</strong>
회원 리포지토리를 먼저 조회하고, 결과 List를 사용해서 중간에 
<code>&lt;tr&gt;</code>, <code>&lt;td&gt;</code> 등의 HTML 태그를 반복해서 출력하고 있습니다.</p>
<hr>
<p><strong>실행 결과</strong>
<img src="https://velog.velcdn.com/images/coding_goat/post/f0d8512d-dd03-4bb4-aa67-bb0d86be7aa4/image.png" alt=""></p>
<hr>
<h3 id="결론">결론</h3>
<p>서블릿으로 개발할 때는 뷰(View)화면을 위한 HTML을 만드는 작업이 자바 코드에 섞여서 지저분하고 복잡했습니다. 하지만, 이제 JSP를 사용한 덕에 뷰를 생성하는 HTML 작업이 깔끔해지고, 중간중간 동적으로 변경이 필요한 부분에는 자바코드를 적용해서 사용했습니다.</p>
<p>하지만, 이것이 과연 깔끔한 코드일까요? </p>
<p>회원 저장 JSP를 보면, 코드의 윗 부분은 회원을 저장하기 위한 비즈니스 로직이고, 코드의 아랫부분은 HTML로 보여주기 위한 뷰 영역입니다. 회원 목록도 마찬가지이고요! 이렇게 되면 JAVA 코드, 데이터를 조회하는 레포지토리 등의 다양한 코드가 JSP에 노출되게 됩니다. </p>
<p>즉, JSP가 너무 많은 역할을 하게 된다는 것이죠. View의 수정할 부분이 있으면 JSP 코드를 건드려야하고, 비즈니스 로직을 수정해야하면 JSP코드를 건드려야 합니다. 이렇게 되면 결국, 유지보수를 하는데에 있어서 힘들어질 수 밖에 없습니다. 그래서 이걸 해결하기 위해서 MVC 패턴이 등장하게 된 것입니다. </p>
<hr>
<p>지금까지의 포스트들을 정리하자면, 웹 애플리케이션 개발에서는 유지보수성과 효율성을 높이기 위해 끊임없이 새로운 기술이 등장해왔습니다. </p>
<blockquote>
</blockquote>
<p>1️⃣ <strong>서블릿</strong>을 사용하면 HTTP 요청과 응답을 직접 처리할 수 있지만, HTML을 자바 코드 안에서 작성해야 하기 때문에 <strong>유지보수가 어렵고, 비효율적인 코드가 많아지는 단점</strong>이 있었습니다.
2️⃣ 이를 보완하기 위해 <strong>JSP</strong>가 등장하여 HTML과 Java 코드를 분리할 수 있게 되었지만, 여전히 <strong>비즈니스 로직과 뷰(View) 코드가 뒤섞이는 문제</strong>가 남아 있었습니다.
3️⃣ 이런 문제를 해결하기 위해 <strong>MVC(Model-View-Controller) 패턴</strong>이 도입되었고, 이를 기반으로 스프링 프레임워크가 발전하면서 보다 <strong>구조적이고 유지보수하기 쉬운 웹 애플리케이션 개발</strong>이 가능해졌습니다.</p>
<p>즉, <strong>서블릿 → JSP → MVC 패턴</strong>으로 발전하면서, 웹 애플리케이션은 점점 더 효율적이고 확장 가능한 구조를 갖추게 되었습니다.</p>
<p>다음 포스트에서는 <strong>MVC 패턴을 적용하여 리팩토링하는과정</strong>을 포스팅 하겠습니다!</p>
<p>다음 포스트에서 만나요!!! </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[서블릿(Servlet)을 활용한 간단한 회원 관리 시스템 구현]]></title>
            <link>https://velog.io/@coding_goat/%EC%84%9C%EB%B8%94%EB%A6%BFServlet%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EA%B0%84%EB%8B%A8%ED%95%9C-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@coding_goat/%EC%84%9C%EB%B8%94%EB%A6%BFServlet%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EA%B0%84%EB%8B%A8%ED%95%9C-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Fri, 31 Jan 2025 06:52:46 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요! 이전 포스트에서 서블릿을 구현하여 간단한 요청과 응답을 하는 예제를 살펴봤는데요. 하지만 실제 웹 애플리케이션에서는 요청과 응답만으로는 부족하며, 보다 복잡한 기능을 효과적으로 관리할 필요가 있습니다.</p>
<p>오늘은 서블릿을 활용하여 간단한 회원 관리 시스템을 구현해보겠습니다. 이를 통해 웹 애플리케이션의 기본 동작 원리를 이해할 수 있을 뿐만 아니라, 왜 이후에 템플릿 엔진과 같은 도구들이 등장하게 되었는지도 자연스럽게 알게 될 것입니다.</p>
<hr>
<h3 id="1-회원member-도메인-객체-생성-및-설명">1. 회원(Member) 도메인 객체 생성 및 설명</h3>
<p>회원 관리 시스템을 구현하기 위해 먼저 <strong>회원</strong>을 나타내는 도메인 클래스를 만들어야 합니다. 도메인 클래스는 애플리케이션에서 중요한 데이터를 담고 있으며, 객체의 속성과 동작을 정의합니다.</p>
<p>아래는 회원 정보를 저장할 <code>Member</code> 클래스로, 회원의 <code>id</code>, <code>username</code>, <code>age</code> 정보를 관리합니다. 또한, 객체의 생성과 데이터 관리를 간편하게 하기 위해 <strong>롬복(Lombok) 라이브러리</strong>를 활용하여 코드의 간결성을 높였습니다.</p>
<pre><code>package hello.servlet.domain.member;

import lombok.Data;

@Data
public class Member {

    private Long id;        // 회원의 고유 ID
    private String username; // 회원 이름
    private int age;         // 회원 나이

    // 기본 생성자 (자바 빈 규약을 위해 필요)
    public Member() {
    }

    // 모든 필드를 초기화하는 생성자
    public Member(String username, int age) {
        this.username = username;
        this.age = age;
    }
}
</code></pre><h4 id="코드-설명">코드 설명</h4>
<ol>
<li>필드 정의</li>
</ol>
<ul>
<li><code>id</code> : 회원의 고유 식별자 (자동 증가 또는 DB 관리)</li>
<li><code>username</code> : 사용자의 이름</li>
<li><code>age</code> : 사용자의 나이</li>
</ul>
<ol start="2">
<li>생성자</li>
</ol>
<ul>
<li>기본 생성자 : 객체를 생성한 후 나중에 데이터를 설정할 수 있도록 제공.</li>
<li>매개변수 생성자 : 객체 생성 시 <code>username</code>과 <code>age</code> 값을 초기화할 수 있도록 제공.</li>
</ul>
<ol start="3">
<li>롬복(@Data) 활용</li>
</ol>
<ul>
<li><code>@Data</code> 어노테이션은 <strong>getter, setter, toString, equals, hashCode</strong> 메서드를 자동으로 생성해줍니다.</li>
<li>이를 통해 <strong>코드의 간결성</strong>을 유지하고, 수동 작성의 번거로움을 줄일 수 있습니다.</li>
</ul>
<p>이렇게 <code>Member</code> 클래스를 정의하면, 이후 서블릿에서 회원정보를 등록하고 조회할 때 이 객체를 활용할 수 있습니다. 다음 단계에서는 회원을 저장하고 관리할 저장소 클래스를 만들어보겠습니다.</p>
<hr>
<h3 id="2-회원-저장소memberrepository-구현-및-설명">2. 회원 저장소(MemberRepository) 구현 및 설명</h3>
<p>이제 회원 정보를 관리할 저장소 클래스를 구현하겠습니다. <code>MemberRepository</code> 클래스는 회원 객체를 저장하고 조회할 수 있도록 도와줍니다. 현재 구현은 <strong>동시성 문제가 고려되지 않은 단순한 저장소</strong>로, 학습 목적으로 사용됩니다. 실무에서는 동시성을 고려한 <code>ConcurrentHashMap</code>과 <code>AtomicLong</code>을 사용해야 합니다.</p>
<pre><code>import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 동시성 문제가 고려되어 있지 않음, 실무에서는 ConcurrentHashMap, AtomicLong 사용 고려
 */
public class MemberRepository {

    private static Map&lt;Long, Member&gt; store = new HashMap&lt;&gt;(); // static 사용 (회원 정보 저장소)
    private static long sequence = 0L; // static 사용 (회원 ID 자동 증가 시퀀스)
    private static final MemberRepository instance = new MemberRepository();

    // 싱글톤 패턴 적용 (하나의 인스턴스만 사용)
    public static MemberRepository getInstance() {
        return instance;
    }

    private MemberRepository() {
    }

    /**
     * 회원 저장
     */
    public Member save(Member member) {
        member.setId(++sequence); // 시퀀스를 증가하여 ID 할당
        store.put(member.getId(), member); // 저장소에 회원 추가
        return member;
    }

    /**
     * ID를 통해 회원 찾기
     */
    public Member findById(Long id) {
        return store.get(id); // 저장소에서 ID로 회원 검색
    }

    /**
     * 모든 회원 조회
     */
    public List&lt;Member&gt; findAll() {
        return new ArrayList&lt;&gt;(store.values()); // 모든 회원 정보를 리스트로 반환
    }

    /**
     * 저장소 초기화 (테스트용)
     */
    public void clearStore() {
        store.clear(); // 저장소 초기화
    }
}
</code></pre><h4 id="코드-설명-1">코드 설명</h4>
<ol>
<li>싱글톤 패턴 적용</li>
</ol>
<ul>
<li>현재 <code>MemberRepository</code>는 <strong>싱글톤 패턴</strong>을 적용하여 애플리케이션 전반에서 <strong>하나의 인스턴스만 유지</strong>하도록 설계되었습니다.</li>
<li><code>private static final MemberRepository instance = new MemberRepository();</code> </li>
<li>-&gt; 애플리케이션 실행 중 여러번 인스턴스가 생성되지 않도록 방지합니다.</li>
<li><code>getInstance()</code> 메서드를 통해 단 하나의 저장소 인스턴스만 사용하도록 보장합니다.</li>
<li>생성자를 <code>private</code>으로 선언하여 외부에서 객체 생성을 막습니다.</li>
</ul>
<blockquote>
<p>💡 <strong>싱글톤 패턴을 사용하는 이유 ??</strong></p>
</blockquote>
<ol>
<li>하나의 저장소 인스턴스를 유지하여 데이터 일관성 보장.</li>
<li>불필요한 객체 생성을 방지하고, 메모리 절약.</li>
<li>애플리케이션 전체에서 동일한 저장소를 공유.</li>
</ol>
<blockquote>
<p>💡 <strong>하지만, 스프링부트를 사용하면 싱글톤 패턴을 직접 구현할 필요가 없습니다.</strong>
스프링 부트는 @Component, @Service, @Repository 등의 애너테이션을 통해 자동으로 <strong>싱글톤 빈(Bean)</strong>을 생성하고 관리합니다.</p>
</blockquote>
<ol start="2">
<li>저장소 구성</li>
</ol>
<ul>
<li><code>store</code> : 회원 정보를 저장하는 <code>HashMap</code> (Key: 회원 ID, Value: <code>Member</code> 객체)</li>
<li><code>sequence</code> : 회원의 ID를 자동 증가시키는 역할</li>
</ul>
<ol start="3">
<li>주요 메서드</li>
</ol>
<ul>
<li><code>save(Member member)</code> : 
회원 객체를 저장하고 고유한 ID를 부여</li>
<li><code>findById(Long id)</code> :
ID를 이용하여 회원 검색</li>
<li><code>findAll()</code> :
저장소에 저장된 모든 회원을 반환.</li>
<li><code>clearStore()</code> :
저장소를 비워 테스트 시 유용하게 사용.</li>
</ul>
<p>(회원 저장소 테스트 코드는 다른 포스트에서 작성하겠습니다!!)</p>
<hr>
<h3 id="3-회원-등록-폼-서블릿-구현">3. 회원 등록 폼 서블릿 구현</h3>
<p>회원 정보를 등록하기 위해 웹 브라우저에서 입력할 수 있는 HTML 폼을 제공하는 서블릿을 구현했습니다.
이 서블릿은 클라이언트가 <code>GET</code> 요청을 보내면 HTML 페이지를 생성하여 응답합니다.</p>
<pre><code>package hello.servlet.web.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

@WebServlet(name = &quot;memberFormServlet&quot;, urlPatterns = &quot;/servlet/members/newform&quot;)
public class MemberFormServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setContentType(&quot;text/html&quot;);
        response.setCharacterEncoding(&quot;utf-8&quot;);

        PrintWriter w = response.getWriter();
        w.write(&quot;&lt;!DOCTYPE html&gt;\n&quot; +
                &quot;&lt;html&gt;\n&quot; +
                &quot;&lt;head&gt;\n&quot; +
                &quot;    &lt;meta charset=\&quot;UTF-8\&quot;&gt;\n&quot; +
                &quot;    &lt;title&gt;회원 가입&lt;/title&gt;\n&quot; +
                &quot;&lt;/head&gt;\n&quot; +
                &quot;&lt;body&gt;\n&quot; +
                &quot;&lt;h1&gt;회원 가입 폼&lt;/h1&gt;\n&quot; +
                &quot;&lt;form action=\&quot;/servlet/members/save\&quot; method=\&quot;post\&quot;&gt;\n&quot; +
                &quot;    username: &lt;input type=\&quot;text\&quot; name=\&quot;username\&quot; /&gt;\n&quot; +
                &quot;    age:      &lt;input type=\&quot;text\&quot; name=\&quot;age\&quot; /&gt;\n&quot; +
                &quot;    &lt;button type=\&quot;submit\&quot;&gt;전송&lt;/button&gt;\n&quot; +
                &quot;&lt;/form&gt;\n&quot; +
                &quot;&lt;/body&gt;\n&quot; +
                &quot;&lt;/html&gt;\n&quot;);
    }
}</code></pre><h4 id="코드-설명-2">코드 설명</h4>
<ol>
<li>서블릿 매핑(<code>@WebServlet</code>)<pre><code>@WebServlet(name = &quot;memberFormServlet&quot;, urlPatterns = &quot;/servlet/members/newform&quot;)
</code></pre></li>
</ol>
<pre><code>- `name` : 서블릿의 이름 지정
- `urlPatterns`: 클라이언트가 `/servlet/members/newform` 경로로 접근 시 서블릿이 실행됨
---
2. HTTP 응답 설정</code></pre><p>response.setContentType(&quot;text/html&quot;);
response.setCharacterEncoding(&quot;utf-8&quot;);</p>
<pre><code>- `text/html` : 응답이 HTML 문서임을 브라우저에게 알림.
- `utf-8` : 한글 등 다양한 문자를 정상적으로 표시할 수 있도록 인코딩.
---
3. HTML 폼 작성 및 응답</code></pre><p>PrintWriter w = response.getWriter();
w.write(&quot;<!DOCTYPE html>\n&quot; +
        &quot;<html>\n&quot; +
        &quot;<head>\n&quot; +
        &quot;    &lt;meta charset=&quot;UTF-8&quot;&gt;\n&quot; +
        &quot;    <title>회원 가입</title>\n&quot; +
        &quot;</head>\n&quot; +
        &quot;<body>\n&quot; +
        &quot;<h1>회원 가입 폼</h1>\n&quot; +
        &quot;&lt;form action=&quot;/servlet/members/save&quot; method=&quot;post&quot;&gt;\n&quot; +
        &quot;    username: &lt;input type=&quot;text&quot; name=&quot;username&quot; /&gt;\n&quot; +
        &quot;    age:      &lt;input type=&quot;text&quot; name=&quot;age&quot; /&gt;\n&quot; +
        &quot;    &lt;button type=&quot;submit&quot;&gt;전송</button>\n&quot; +
        &quot;</form>\n&quot; +
        &quot;</body>\n&quot; +
        &quot;</html>\n&quot;);</p>
<pre><code>
- `PrintWriter`를 사용하여 클라이언트에게 HTML을 응답.
- 폼의 `action` 속성 `/servlet/members/save`로 지정하여 데이터를 POST 방식으로 전송.
---
#### 실행 결과
![](https://velog.velcdn.com/images/coding_goat/post/ddefaa07-4927-4be6-9f71-921d70c22f82/image.png)

---
### 4. 회원 저장 서블릿 구현

이제 **회원 정보를 입력받아 저장하는 기능을 구현**하겠습니다. 이 서블릿은 **회원 가입 폼에서 전송된 데이터를 받아 저장**하고, **성공 메시지를 응답하는 역할**을 합니다.
</code></pre><p>package hello.servlet.web.servlet;</p>
<p>import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;</p>
<p>import java.io.IOException;
import java.io.PrintWriter;</p>
<p>@WebServlet(name = &quot;memberSaveServlet&quot;, urlPatterns = &quot;/servlet/members/save&quot;)
public class MemberSaveServlet extends HttpServlet {</p>
<pre><code>private MemberRepository memberRepository = MemberRepository.getInstance();

@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

    System.out.println(&quot;MemberSaveServlet.service&quot;);

    // 1. 요청 데이터 받기
    String username = request.getParameter(&quot;username&quot;);
    int age = Integer.parseInt(request.getParameter(&quot;age&quot;));

    // 2. Member 객체 생성 및 저장
    Member member = new Member(username, age);
    System.out.println(&quot;member = &quot; + member);
    memberRepository.save(member);

    // 3. 응답 설정
    response.setContentType(&quot;text/html&quot;);
    response.setCharacterEncoding(&quot;utf-8&quot;);

    // 4. 저장 결과 HTML 응답
    PrintWriter w = response.getWriter();
    w.write(&quot;&lt;html&gt;\n&quot; +
            &quot;&lt;head&gt;\n&quot; +
            &quot;    &lt;meta charset=\&quot;UTF-8\&quot;&gt;\n&quot; +
            &quot;&lt;/head&gt;\n&quot; +
            &quot;&lt;body&gt;\n&quot; +
            &quot;&lt;h1&gt;회원 저장 성공&lt;/h1&gt;\n&quot; +
            &quot;&lt;ul&gt;\n&quot; +
            &quot;    &lt;li&gt;ID: &quot; + member.getId() + &quot;&lt;/li&gt;\n&quot; +
            &quot;    &lt;li&gt;Username: &quot; + member.getUsername() + &quot;&lt;/li&gt;\n&quot; +
            &quot;    &lt;li&gt;Age: &quot; + member.getAge() + &quot;&lt;/li&gt;\n&quot; +
            &quot;&lt;/ul&gt;\n&quot; +
            &quot;&lt;a href=\&quot;/index.html\&quot;&gt;메인으로&lt;/a&gt;\n&quot; +
            &quot;&lt;/body&gt;\n&quot; +
            &quot;&lt;/html&gt;&quot;);
}</code></pre><p>}</p>
<pre><code>
#### 코드 설명
1. `@WebServlet`을 이용한 서블릿 등록</code></pre><p>@WebServlet(name = &quot;memberSaveServlet&quot;, urlPatterns = &quot;/servlet/members/save&quot;)</p>
<pre><code>
- 클라이언트가 `/servlet/members/save` 경로로 `POST` 요청을 보내면 이 서블릿이 실행됨.
- `name=&quot;memberSaveServlet&quot;`은 서블릿의 이름을 명시 (필수 아님).

---
2. `service()` 메서드에서 요청 처리</code></pre><p>protected void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {</p>
<pre><code>
- 클라이언트의 HTTP 요청을 처리하는 메서드.
- `HttpServletRequest request` : 클라이언트가 보낸 요청 정보.
- `HttpServletResponse response` : 클라이언트에게 응답을 보낼 객체.
---
3. 요청 데이터 받기</code></pre><p>String username = request.getParameter(&quot;username&quot;);
int age = Integer.parseInt(request.getParameter(&quot;age&quot;));</p>
<pre><code>- 클라이언트가 보낸 `username`과 `age` 값을 `request.getParameter()`로 가져옴.
- `getParameter()`는 항상 문자열을 반환하므로, `age` 값은 `Integer.parseInt()`를 사용하여 변환.
---
4. 회원 객체 생성 및 저장</code></pre><p>Member member = new Member(username, age);
System.out.println(&quot;member = &quot; + member);
memberRepository.save(member);</p>
<pre><code>
- 받아온 데이터를 이용해 `Member` 객체를 생성.
- `memberRepository.save(member)`를 통해 저장.
---

5. 응답 데이터 설정</code></pre><p>response.setContentType(&quot;text/html&quot;);
response.setCharacterEncoding(&quot;utf-8&quot;);</p>
<pre><code>
- 응답이 `HTML` 형식임을 명시.
- `utf-8` 설정으로 한글이 깨지지 않도록 설정.

---
6. 회원 저장 성공 메세지 반환</code></pre><p>PrintWriter w = response.getWriter();
w.write(&quot;<html>\n&quot; +
        &quot;<head>\n&quot; +
        &quot;    &lt;meta charset=&quot;UTF-8&quot;&gt;\n&quot; +
        &quot;</head>\n&quot; +
        &quot;<body>\n&quot; +
        &quot;<h1>회원 저장 성공</h1>\n&quot; +
        &quot;<ul>\n&quot; +
        &quot;    <li>ID: &quot; + member.getId() + &quot;</li>\n&quot; +
        &quot;    <li>Username: &quot; + member.getUsername() + &quot;</li>\n&quot; +
        &quot;    <li>Age: &quot; + member.getAge() + &quot;</li>\n&quot; +
        &quot;</ul>\n&quot; +
        &quot;&lt;a href=&quot;/index.html&quot;&gt;메인으로</a>\n&quot; +
        &quot;</body>\n&quot; +
        &quot;</html>&quot;);</p>
<pre><code>- 저장된 회원의 ID, 이름, 나이를 HTML로 출력.
- `getWriter()`를 사용하여 HTML 응답을 직접 생성.
---
#### 실행결과
![](https://velog.velcdn.com/images/coding_goat/post/340bc454-394c-4e9f-ad67-09004578ffad/image.png)

---

### 5. 회원 목록 조회 서블릿
이제 **회원 목록을 조회하는 서블릿**을 구현했습니다. 이 서블릿은 **저장된 모든 회원 정보를 테이블 형식으로 출력**하는 역할을 합니다.
</code></pre><p>package hello.servlet.web.servlet;</p>
<p>import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;</p>
<p>import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;</p>
<p>@WebServlet(name = &quot;memberListServlet&quot;, urlPatterns = &quot;/servlet/members&quot;)
public class MemberListServlet extends HttpServlet {</p>
<pre><code>private MemberRepository memberRepository = MemberRepository.getInstance();

@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

    response.setContentType(&quot;text/html&quot;);
    response.setCharacterEncoding(&quot;utf-8&quot;);

    // 1. 회원 목록 조회
    List&lt;Member&gt; members = memberRepository.findAll();

    // 2. HTML 응답 생성
    PrintWriter w = response.getWriter();
    w.write(&quot;&lt;html&gt;&quot;);
    w.write(&quot;&lt;head&gt;&quot;);
    w.write(&quot;    &lt;meta charset=\&quot;UTF-8\&quot;&gt;&quot;);
    w.write(&quot;    &lt;title&gt;회원 목록&lt;/title&gt;&quot;);
    w.write(&quot;&lt;/head&gt;&quot;);
    w.write(&quot;&lt;body&gt;&quot;);
    w.write(&quot;&lt;h1&gt;회원 목록&lt;/h1&gt;&quot;);
    w.write(&quot;&lt;a href=\&quot;/index.html\&quot;&gt;메인으로&lt;/a&gt;&quot;);
    w.write(&quot;&lt;table border=&#39;1&#39;&gt;&quot;);
    w.write(&quot;    &lt;thead&gt;&quot;);
    w.write(&quot;        &lt;tr&gt;&quot;);
    w.write(&quot;            &lt;th&gt;ID&lt;/th&gt;&quot;);
    w.write(&quot;            &lt;th&gt;Username&lt;/th&gt;&quot;);
    w.write(&quot;            &lt;th&gt;Age&lt;/th&gt;&quot;);
    w.write(&quot;        &lt;/tr&gt;&quot;);
    w.write(&quot;    &lt;/thead&gt;&quot;);
    w.write(&quot;    &lt;tbody&gt;&quot;);

    // 3. 회원 정보 반복 출력
    for (Member member : members) {
        w.write(&quot;    &lt;tr&gt;&quot;);
        w.write(&quot;        &lt;td&gt;&quot; + member.getId() + &quot;&lt;/td&gt;&quot;);
        w.write(&quot;        &lt;td&gt;&quot; + member.getUsername() + &quot;&lt;/td&gt;&quot;);
        w.write(&quot;        &lt;td&gt;&quot; + member.getAge() + &quot;&lt;/td&gt;&quot;);
        w.write(&quot;    &lt;/tr&gt;&quot;);
    }

    w.write(&quot;    &lt;/tbody&gt;&quot;);
    w.write(&quot;&lt;/table&gt;&quot;);
    w.write(&quot;&lt;/body&gt;&quot;);
    w.write(&quot;&lt;/html&gt;&quot;);
}</code></pre><p>}</p>
<pre><code>
#### 코드 설명

1. `@WebServlet`을 이용한 서블릿 등록</code></pre><p>@WebServlet(name = &quot;memberListServlet&quot;, urlPatterns = &quot;/servlet/members&quot;)</p>
<pre><code>- 클라이언트가 `/servlet/members` 경로로 `GET` 요청을 보내면 이 서블릿이 실행됨.
---

2. HTTP 응답 설정</code></pre><p>response.setContentType(&quot;text/html&quot;);
response.setCharacterEncoding(&quot;utf-8&quot;);</p>
<pre><code>- `text/html`: 응답이 HTML 형식임을 브라우저에게 알림.
- `utf-8`: 한글이 깨지지 않도록 인코딩 설정.
---
3. 저장된 회원 목록 조회</code></pre><p>List<Member> members = memberRepository.findAll();</p>
<pre><code>
- `memberRepository.findAll()`을 통해 저장된 모든 회원 데이터를 가져옴.
---
4. `PrintWriter`를 사용하여 동적으로 HTML을 생성하고 응답.</code></pre><p>w.write(&quot;<table border='1'>&quot;);
w.write(&quot;    <thead>&quot;);
w.write(&quot;        <tr>&quot;);
w.write(&quot;            <th>ID</th>&quot;);
w.write(&quot;            <th>Username</th>&quot;);
w.write(&quot;            <th>Age</th>&quot;);
w.write(&quot;        </tr>&quot;);
w.write(&quot;    </thead>&quot;);</p>
<pre><code>- 회원 정보를 표(table) 형태로 출력.

--- 
6. 회원 정보 동적 출력</code></pre><p>for (Member member : members) {
    w.write(&quot;    <tr>&quot;);
    w.write(&quot;        <td>&quot; + member.getId() + &quot;</td>&quot;);
    w.write(&quot;        <td>&quot; + member.getUsername() + &quot;</td>&quot;);
    w.write(&quot;        <td>&quot; + member.getAge() + &quot;</td>&quot;);
    w.write(&quot;    </tr>&quot;);
}</p>
<p>```</p>
<ul>
<li><code>for</code> 문을 이용해 <code>List&lt;Member&gt;</code>에 저장된 모든 회원 정보를 반복 출력.</li>
</ul>
<hr>
<h4 id="실행결과">실행결과</h4>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/4e790bfe-c30e-4461-9830-a80bd3957b74/image.png" alt=""></p>
<hr>
<h3 id="결론">결론</h3>
<p>지금까지 서블릿과 자바만으로 HTML을 만들어보았습니다. 서블릿 덕분에 동적(ex. 멤버가 늘어나면 멤버리스트에 따라서 테이블이 늘어나는 것을 동적이라 함.)으로 원하는 HTML을 마음껏 만들 수 있습니다. 정적인 HTML 문서라면 화면이 계속 달라지는 회원의 저장 결과라던가, 회원 목록 같은 동적인 HTML을 만드는 일은 불가능 할 것입니다.</p>
<p> 하지만, 코드에서 보이듯이 이것은 매우 복잡하고 비효율적입니다. 자바코드로 HTML을 만들어 내는 것보다 HTML문서에 동적으로 변경해야 하는 부분만 자바코드를 넣는것이 훨씬 편리합니다. <strong>이것이 바로 템플릿 엔진(JSP,Thymeleaf,Freemarker 등등) 이 나온 이유입니다.</strong>
따라서, 다음 포스트는 JSP, 더 나아가 스프링과 통합이 잘되는 Thymeleaf를 사용하여 동일한 작업을 진행하는 포스트를 올리겠습니다. 다음시간에 뵈어요~!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[서블릿(Servlet) 요청과 응답 : 코드로 배우기!!]]></title>
            <link>https://velog.io/@coding_goat/%EC%84%9C%EB%B8%94%EB%A6%BFServlet-%EC%9A%94%EC%B2%AD%EA%B3%BC-%EC%9D%91%EB%8B%B5-%EC%BD%94%EB%93%9C%EB%A1%9C-%EB%B0%B0%EC%9A%B0%EA%B8%B0</link>
            <guid>https://velog.io/@coding_goat/%EC%84%9C%EB%B8%94%EB%A6%BFServlet-%EC%9A%94%EC%B2%AD%EA%B3%BC-%EC%9D%91%EB%8B%B5-%EC%BD%94%EB%93%9C%EB%A1%9C-%EB%B0%B0%EC%9A%B0%EA%B8%B0</guid>
            <pubDate>Mon, 20 Jan 2025 07:49:01 GMT</pubDate>
            <description><![CDATA[<p>서블릿의 개념을 이해했다면, 이제 실제로 서블릿을 구현하여 클라이언트의 요청을 처리하고 응답을 반환하는 과정을 살펴볼 차례입니다.</p>
<p>이번 포스트에서는 서블릿을 통해 웹 브라우저의 요청을 받아들이고, 파라미터 및 헤더 정보를 처리한 후, 클라이언트에게 응답을 반환하는 방법을 실습해보겠습니다.</p>
<p>다음과 같은 순서로 포스트를 할 예정입니다!</p>
<ul>
<li>HTTP 요청 파라미터 처리 방법</li>
<li>HTTP 요청 헤더 조회 및 활용</li>
<li>GET 및 POST 요청 처리 방식</li>
<li>클라이언트에게 적절한 응답을 반환하는 방법</li>
</ul>
<hr>
<h3 id="1-http-요청-파라미터-처리-방법">1. HTTP 요청 파라미터 처리 방법</h3>
<p>클라이언트가 URL에 포함한 쿼리 파라미터를 처리하는 방법을 살펴보겠습니다.</p>
<p><strong>예제: 요청 파라미터 처리</strong> (getParameter)</p>
<pre><code>import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = &quot;paramServlet&quot;, urlPatterns = &quot;/param&quot;)
public class ParamServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 단일 파라미터 조회
        String username = request.getParameter(&quot;username&quot;);
        String age = request.getParameter(&quot;age&quot;);

        response.setContentType(&quot;text/plain&quot;);
        response.getWriter().write(&quot;Username: &quot; + username + &quot;\n&quot;);
        response.getWriter().write(&quot;Age: &quot; + age + &quot;\n&quot;);

        // 복수 파라미터 조회
        String[] hobbies = request.getParameterValues(&quot;hobby&quot;);
        for (String hobby : hobbies) {
            response.getWriter().write(&quot;Hobby: &quot; + hobby + &quot;\n&quot;);
        }
    }
}
</code></pre><p><strong>요청예시</strong> :
<code>http://localhost:8080/param?username=John&amp;age=25&amp;hobby=reading&amp;hobby=cycling</code></p>
<p><strong>응답예시</strong> :</p>
<pre><code>Username: John
Age: 25
Hobby: reading
Hobby: cycling
</code></pre><hr>
<h3 id="2-http-요청-헤더-조회-및-활용">2. HTTP 요청 헤더 조회 및 활용</h3>
<p>클라이언트가 전송한 요청 헤더를 조회하고, 필요한 정보를 추출하는 방법을 살펴보겠습니다.</p>
<p><strong>예제: 요청 헤더 조회</strong> (getHeader)</p>
<pre><code>import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;

@WebServlet(name = &quot;headerServlet&quot;, urlPatterns = &quot;/headers&quot;)
public class HeaderServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType(&quot;text/plain&quot;);

        // 모든 헤더 출력
        Enumeration&lt;String&gt; headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            response.getWriter().write(headerName + &quot;: &quot; + request.getHeader(headerName) + &quot;\n&quot;);
        }

        // 특정 헤더 조회
        String userAgent = request.getHeader(&quot;User-Agent&quot;);
        response.getWriter().write(&quot;User-Agent: &quot; + userAgent + &quot;\n&quot;);
    }
}
</code></pre><p><strong>요청 예시</strong> : 
브라우저에서 <code>http://localhost:8080/headers</code> 요청 시</p>
<p><strong>응답 예시</strong> : </p>
<pre><code>host: localhost:8080
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64)
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64)

</code></pre><hr>
<h3 id="3-get-및-post-요청-처리-방식">3. GET 및 POST 요청 처리 방식</h3>
<p>서블릿에서 클라이언트의 요청 메서드(GET,POST)에 따라 다르게 처리하는 방법을 살펴보겠습니다.</p>
<p><strong>예제: GET 및 POST 요청 처리</strong></p>
<pre><code>import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = &quot;methodServlet&quot;, urlPatterns = &quot;/method&quot;)
public class MethodServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType(&quot;text/html&quot;);
        response.getWriter().write(&quot;&lt;h1&gt;GET 요청을 처리합니다.&lt;/h1&gt;&quot;);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType(&quot;text/html&quot;);
        String name = request.getParameter(&quot;name&quot;);
        response.getWriter().write(&quot;&lt;h1&gt;POST 요청: Hello, &quot; + name + &quot;!&lt;/h1&gt;&quot;);
    }
}
</code></pre><p><strong>요청 예시</strong> :</p>
<ul>
<li>GET 요청 : <code>http://localhost:8080/method</code></li>
<li>POST 요청(폼 데이터 전송):<pre><code>&lt;form action=&quot;http://localhost:8080/method&quot; method=&quot;POST&quot;&gt;
  &lt;input type=&quot;text&quot; name=&quot;name&quot;&gt;
  &lt;button type=&quot;submit&quot;&gt;Submit&lt;/button&gt;
&lt;/form&gt;
</code></pre></li>
</ul>
<pre><code>
**응답 예시** :
- GET 요청 응답:</code></pre><h1>GET 요청을 처리합니다.</h1>

<pre><code>- POST 요청 응답(name=John):</code></pre><h1>POST 요청: Hello, John!</h1>

<pre><code>
---

### 4. 클라이언트에게 적절한 응답을 반환하는 방법
클라이언트에게 텍스트, JSON, HTML 등의 다양한 응답을 반환하는 방법을 살펴보겠습니다. 클라이언트에게 응답하는 건 요청보다 더 간단합니다. ㅎㅎ
</code></pre><p>import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;</p>
<p>@WebServlet(name = &quot;responseServlet&quot;, urlPatterns = &quot;/response&quot;)
public class ResponseServlet extends HttpServlet {</p>
<pre><code>@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
    // 텍스트 응답
    response.setContentType(&quot;text/plain&quot;);
    response.getWriter().write(&quot;Hello, plain text response!&quot;);

    // JSON 응답
    response.setContentType(&quot;application/json&quot;);
    response.getWriter().write(&quot;{\&quot;message\&quot;: \&quot;Hello, JSON!\&quot;}&quot;);

    // HTML 응답
    response.setContentType(&quot;text/html&quot;);
    response.getWriter().write(&quot;&lt;h1&gt;Hello, HTML Response!&lt;/h1&gt;&quot;);
}</code></pre><p>}</p>
<pre><code>
**요청 예시** : 
`http://localhost:8080/response`

**응답 예시** :
클라이언트가 브라우저에서 요청 시 다양한 포맷을 받아볼 수 있습니다.</code></pre><p>Hello, plain text response!
{&quot;message&quot;: &quot;Hello, JSON!&quot;}</p>
<h1>Hello, HTML Response!</h1>

<p>```</p>
<hr>
<h3 id="5-결론">5. 결론</h3>
<p>서블릿에서 요청을 받고 응답을 처리하는 다양한 방법을 예제를 통해 살펴보았습니다. 서블릿은 웹 애플리케이션의 기본 동작 원리를 이해하는데 중요한 기술입니다. 특히, 스프링 부트의 내부 동작에 대해서 알기 위해서 서블릿이라는 개념은 꼭 알아야합니다. </p>
<p>저 또한, 서블릿에 대한 개념을 모르고 스프링부트로 바로 프로젝트를 진행했던 저는 이러한 의문을 가졌습니다. </p>
<p>&#39;스프링부트로 다 되는거 아닌가?&#39; </p>
<p>저 역시 처음에는 스프링 부트로 프로젝트를 진행하면서 서블릿을 몰라도 충분하다고 생각했습니다. 하지만 스프링부트에 대해서 공부를 하며, 스프링 부트가 자동으로 해주는 부분들을 제대로 이해하지 못한다면, 문제가 발생했을 때 원인을 찾기가 어렵고, 해결 과정도 더딜 수 있다는 것을 깨달았습니다.</p>
<p><strong>결국, 스프링 부트는 서블릿을 자동으로 설정하고 관리해주는 프레임워크라고 보면 됩니다.</strong></p>
<p>스프링 부트는 서블릿의 복잡한 설정을 자동화하여 개발자가 더욱 효율적으로 웹 애플리케이션을 개발할 수 있도록 돕지만, 서블릿의 동작 원리를 알고 나면 스프링 부트가 어떤 방식으로 요청을 처리하고 응답을 생성하는지 더 잘 이해할 수 있습니다.</p>
<p>서블릿을 배우고 나서야 비로소 스프링 부트가 웹 애플리케이션을 얼마나 간편하게 만들어주는지 실감할 수 있었습니다.</p>
<p>다음 포스트에서는 <strong>스프링 부트가 서블릿을 자동으로 설정하는 과정</strong>과 <strong>스프링 MVC의 동작 원리</strong>를 통해 두 개념을 어떻게 연결할 수 있는지 알아보겠습니다.</p>
<p>다음 포스트에서 뵈어요 !!!!!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[서블릿(Servlet)이란? : 웹 애플리케이션의 핵심 개념 정리]]></title>
            <link>https://velog.io/@coding_goat/%EC%84%9C%EB%B8%94%EB%A6%BFServlet%EC%9D%B4%EB%9E%80-%EC%9B%B9-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%9D%98-%ED%95%B5%EC%8B%AC-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@coding_goat/%EC%84%9C%EB%B8%94%EB%A6%BFServlet%EC%9D%B4%EB%9E%80-%EC%9B%B9-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%9D%98-%ED%95%B5%EC%8B%AC-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 20 Jan 2025 07:05:48 GMT</pubDate>
            <description><![CDATA[<h3 id="1-서블릿이란">1. 서블릿이란?</h3>
<p><strong>서블릿(Servlet)</strong>은 자바 웹 애플리케이션에서 클라이언트의 요청을 처리하고, 응답을 반환하는 서버측 프로그램입니다. </p>
<hr>
<h4 id="1-1-서블릿의-특징">1-1. 서블릿의 특징</h4>
<blockquote>
</blockquote>
<ol>
<li><strong>자바 기반</strong> : 자바 언어로 개발되어 JVM 환경에서 실행됨.</li>
<li><strong>서버 측 컴포넌트</strong> : 클라이언트(브라우저, 모바일 앱 등)의 요청을 받아 서버에서 처리하고 응답함.</li>
<li><strong>라이프사이클 관리</strong> : 서블릿은 웹 컨테이너에 의해 생성, 초기화, 서비스, 소멸의 라이프사이클을 가짐.</li>
<li><strong>멀티쓰레드 지원</strong> : 하나의 서블릿 인스턴스가 여러 클라이언트의 요청을 동시에 처리할 수 있음.</li>
<li><strong>플랫폼 독립적</strong> : 자바 기반이므로 다양한 OS 환경에서 실행 가능.</li>
</ol>
<hr>
<h3 id="2-서블릿-컨테이너servlet-container란">2. 서블릿 컨테이너(Servlet Container)란?</h3>
<p>서블릿 컨테이너는 <strong>서블릿의 실행 환경을 제공하는 서버 구성 요소</strong>입니다. 즉, 서블릿을 관리하고, 요청이 들어오면 서블릿을 실행해 응답을 생성하는 역할을 수행합니다.</p>
<h4 id="2-1-서블릿-컨테이너의-역할">2-1. 서블릿 컨테이너의 역할</h4>
<blockquote>
<ol>
<li><strong>서블릿의 생성 및 관리</strong> : 클라이언트 요청이 있을 때 서블릿 객체를 생성하고, 필요 시 재사용.</li>
<li><strong>HTTP 요청과 응답 처리</strong> : 클라이언트의 요청을 받아 서블릿으로 전달, 서브릿의 응답을 클라이언트에게 반환.</li>
<li><strong>쓰레드 관리 및 동시성 처리</strong> : 멀티쓰레드 환경에서 여러 요청을 동시에 처리.</li>
<li><strong>보안 및 세션 관리</strong> : 세션 및 쿠키를 관리하여 사용자 상태 유지.</li>
<li><strong>로깅 및 에러 처리</strong> : 웹 요청 및 서블릿 실행 과정에서 발생하는 로깅 및 예외 처리.</li>
</ol>
</blockquote>
<hr>
<h3 id="3-서블릿과-서블릿-컨테이너의-관계">3. 서블릿과 서블릿 컨테이너의 관계</h3>
<ul>
<li>서블릿은 <strong>클라이언트의 요청을 처리하는 로직</strong>을 작성하는 역할을 함.</li>
<li>서블릿 컨테이너는 <strong>서블릿의 실행을 관리</strong>하고, 요청이 들어오면 서블릿을 호출하여 응답을 처리.</li>
</ul>
<hr>
<h3 id="4-서블릿과-서블릿-컨테이너의-동작-흐름">4. 서블릿과 서블릿 컨테이너의 동작 흐름</h3>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/968e68bc-52ef-4643-8b3a-9be6f30e9cf6/image.png" alt=""></p>
<blockquote>
</blockquote>
<ol>
<li>클라이언트(웹 브라우저)가 HTTP 요청을 보냄.</li>
<li>웹 서버(톰캣)가 해당 요청을 서블릿 컨테이너로 전달.</li>
<li>서블릿 컨테이너는 해당 요청을 처리할 서블릿을 찾고 실행.</li>
<li>서블릿이 비즈니스 로직을 수행하고 응답을 생성.</li>
<li>서블릿 컨테이너가 응답을 웹 서버로 전달.</li>
<li>웹 서버가 클라이언트에게 최종 응답 전송.</li>
</ol>
<hr>
<p>정리하자면, <strong>서블릿</strong>은 웹 요청을 처리하는 자바 객체이며, <strong>서블릿 컨테이너</strong>는 서블릿을 실행하고 관리하는 환경이라고 생각하면 됩니다! 다음 포스트에서는 서블릿을 활용하여 Http 요청, 응답을 받는 예제에 대한 포스트를 하겠습니다! 다음 포스트에서 뵈어요~!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[WS(Web Server) 와 WAS(Web Application Server)]]></title>
            <link>https://velog.io/@coding_goat/WSWeb-Server-%EC%99%80-WASWeb-Application-Server</link>
            <guid>https://velog.io/@coding_goat/WSWeb-Server-%EC%99%80-WASWeb-Application-Server</guid>
            <pubDate>Mon, 06 Jan 2025 07:04:37 GMT</pubDate>
            <description><![CDATA[<p>이번 포스트는 WS(Web Server)와 WAS(Web Application Server)가 무엇인지, 차이와 역할에 대해  설명하겠습니다. </p>
<hr>
<h3 id="1-ws-web-server">1. WS (Web Server)</h3>
<p> <strong>WS의 역할</strong></p>
<ul>
<li>웹 서버는 클라이언트의 HTTP 요청 기반으로 동작</li>
<li>정적(파일) HTML, CSS, JS, 이미지, 영상을 처리</li>
<li>동적 요청을 WAS로 전달하는 역할</li>
<li>ex) NGINX, APACHE</li>
</ul>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/b483de5d-9cdc-4754-84f9-c63eb80257f7/image.png" alt=""></p>
<p>정리하자면, WS(Web Server)는 HTML, CSS, JavaScript, 이미지 등 정적 콘텐츠를 처리하고, 동적 요청은 WAS(Web Application Server)에 전달하는 역할을 합니다.</p>
<hr>
<h3 id="2-was-web-application-server">2. WAS (Web Application Server)</h3>
<p><strong>WAS의 역할</strong></p>
<ul>
<li>HTTP 기반으로 동작</li>
<li>동적 컨텐츠(db에서 가져온 정보, API 요청 처리 결과 등)을 생성하고 반환 + (정적 컨텐츠 제공 가능)</li>
<li>프로그램 코드를 실행해서 애플리케이션 로직 수행</li>
<li>ex) 톰캣(Tomcat) Jetty, Undertow</li>
</ul>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/2a1a5238-386a-4ec9-a7db-ddc26f7429a5/image.png" alt=""></p>
<p>정리하자면, WAS(Web Application Server)는 클라이언트의 동적 요청을 처리하고 비즈니스 로직을 실행하여 결과를 반환하는 서버입니다.</p>
<hr>
<h3 id="3-ws와-was">3. WS와 WAS</h3>
<ul>
<li>WS는 정적 리소스(파일)를 제공, WAS는 애플리케이션 로직을 처리</li>
<li>사실은 둘의 용어도 경계도 모호합니다. (WS도 애플리케이션 로직을 처리하기도 하며, WAS는 WS의 기능을 제공하기 때문)</li>
<li>WAS는 애플리케이션 코드를 실행하는데 더 특화되어 있습니다.</li>
</ul>
<p>밑의 그림처럼, WAS, DB만으로 시스템 구성이 가능합니다. 
(WAS가 정적 리소스, 애플리케이션 로직을 모두 제공이 가능하기에)</p>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/d84edeb3-f811-4f92-b502-0157f4dc5226/image.png" alt=""></p>
<p>그럼, 이렇게 WAS로 애플리케이션 로직, 정적컨텐츠 제공을 하면 되지않냐? 라고 생각이 드실 수 있습니다.</p>
<p><strong>하지만, 이런 방식의 웹 시스템 구성은 WAS가 너무 많은 역할을 담당하게 되어, 서버 과부하 우려가 발생할 수 있습니다. 또한, WAS에서 돌아가야 할 애플리케이션 로직이 정적 리소스 때문에 WAS의 버그가 발생할 수 있습니다. 이렇게 WAS에 장애가 발생하면, 정적컨텐츠로 제공되어야 하는 오류화면도 노출이 불가능하게 됩니다.</strong></p>
<hr>
<p>따라서, WS와 WAS에 각각 역할을 수행시키는 웹 시스템 구성을 따르는 것이 좋습니다. 밑의 그림처럼 말이죠.</p>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/5941cdc8-f801-46d5-a3a5-2a910cfe5b97/image.png" alt=""></p>
<p>이 구성의 특징을 정리하자면,</p>
<ul>
<li>정적 리소스는 WS가 처리</li>
<li>WS는 동적인 처리가 필요하면 WAS에 요청함</li>
<li>WAS는 애플리케이션 로직 처리 전담</li>
<li>정적 리소스만 제공하는 WS는 잘 죽지 않음</li>
<li>애플리케이션 로직이 동작하는 WAS 서버는 잘 죽음</li>
<li>따라서, WAS, DB 장애시 WS가 오류 화면 제공 가능</li>
</ul>
<hr>
<p>최종적으로, 정적리소스와 애플리케이션 리소스의 사용량에 따라서 WS를 증설하거나, WAS를 증설할 수 있습니다.
<img src="https://velog.velcdn.com/images/coding_goat/post/2721edb0-c0e1-49fa-baff-5ea8bb72a344/image.png" alt=""></p>
<hr>
<p>다음 포스트에서는 WAS의 핵심 컴포넌트인 서블릿과 쓰레드에 대해서 포스트하겠습니다! 다음 포스트에서 봅시다!!  </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[StringBuilder란?]]></title>
            <link>https://velog.io/@coding_goat/StringBuilder%EB%9E%80</link>
            <guid>https://velog.io/@coding_goat/StringBuilder%EB%9E%80</guid>
            <pubDate>Tue, 17 Dec 2024 07:16:59 GMT</pubDate>
            <description><![CDATA[<p>코테 문제를 풀다가, StringBuilder 클래스를 사용하게 되어, StringBuilder 클래스가 어떤것인지 정리하기 위해 작성된 포스트입니다! </p>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/d119a771-707d-47f4-bc38-19db5592d331/image.png" alt=""></p>
<p>우선 코테 문제는 이렇게 간단합니다. </p>
<p>저 문제의 해결과정은,</p>
<blockquote>
</blockquote>
<ol>
<li>long형 n을 문자열로 변환</li>
<li>문자열을 객체형 문자 배열로 변환</li>
<li>변환된 배열에서 내림차순으로 정렬</li>
<li>문자 배열(내림차순된 배열)을 다시 문자열로 변환</li>
<li>문자열을 다시 long형으로 변환</li>
</ol>
<p>이런 프로세스로 진행됩니다.</p>
<p>코드로 나타내면, 이런 코드가 됩니다.</p>
<pre><code>import java.util.Arrays;
import java.util.Collections;

class Solution {
    public long solution(long n) {

        // 1. 숫자를 문자열로 변환
        String s = String.valueOf(n);

        // 2. 문자열을 Character[] 객체형 배열로 변환
        Character[] charArray = new Character[s.length()];
        for (int i = 0; i &lt; s.length(); i++) {
            charArray[i] = s.charAt(i);
        }

        // 3. 내림차순 정렬
        Arrays.sort(charArray, Collections.reverseOrder());

        // 4. 정렬된 문자 배열을 문자열로 변환
        StringBuilder sb = new StringBuilder();
        for (Character c : charArray) {
            sb.append(c); // 문자 하나씩 StringBuilder에 추가
        }

        // 5. 문자열을 다시 long으로 변환
        long answer = Long.parseLong(sb.toString());

        return answer;
    }
}
</code></pre><p>문자열로 변환하는 과정에서 StrigBuilder가 사용된것을 볼 수 있습니다.</p>
<hr>
<h3 id="1-stringbuilder란">1. StringBuilder란?</h3>
<p><code>StringBuilder</code>는 자바에서 문자열을 효율적으로 다룰 수 있도록 제공되는 클래스입니다.
<code>String</code> 클래스(String클래스는 불변) 와 달리 <strong>가변(mutable)</strong> 객체로 문자열을 변경할 때마다 새로운 객체를 생성하지 않고 동일한 객체 안에서 수정합니다. 쉽게 생각하면 문자열을 조작해야 할 때, StringBuilder클래스를 활용하여 조작하면 됩니다.</p>
<hr>
<h3 id="2-stringbuilder-주요-메서드-정리">2. StringBuilder 주요 메서드 정리</h3>
<blockquote>
</blockquote>
<ol>
<li><code>append()</code>
문자열 끝에 추가합니다.<pre><code>StringBuilder sb = new StringBuilder(&quot;Hello&quot;);
sb.append(&quot; Wolrd&quot;);
System.out.println(sb.toString()); // 출력 : Hello World</code></pre></li>
<li><code>insert()</code>
특정 위치에 문자열을 삽입합니다.<pre><code>StringBuilder sb = new StringBuilder(&quot;Hello&quot;);
sb.insert(5, &quot; Java&quot;);
System.out.println(sb.toString()); // 출력 : Hello Java</code></pre></li>
<li><code>delete()</code>
특정 위치의 문자를 삭제합니다.<pre><code>StringBuilder sb = new StringBuilder(&quot;Hello World&quot;);
sb.delete(5, 11); // 인덱스 5부터 10까지 삭제
System.out.println(sb.toString()); // 출력: Hello</code></pre></li>
<li><code>reverse()</code>
문자열을 뒤집습니다.<pre><code>StringBuilder sb = new StringBuilder(&quot;12345&quot;);
sb.reverse();
System.out.println(sb.toString()); // 출력 : 54321</code></pre></li>
<li><code>toString()</code>
<code>StringBuilder</code> 객체를 <strong>문자열(String)</strong>로 변환합니다.</li>
</ol>
<hr>
<h3 id="3-결론">3. 결론</h3>
<p><code>StringBuilder</code>는 문자열을 효율적으로 조작하기 위한 가변 클래스입니다.
문자열을 반복해서 추가, 수정, 삭제하는 작업이 필요할 때 <code>String</code> 대신 <code>StringBuilder</code>를 사용하면 성능을 크게 개선할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Backend] Jwt란?]]></title>
            <link>https://velog.io/@coding_goat/Backend-Jwt%EB%9E%80</link>
            <guid>https://velog.io/@coding_goat/Backend-Jwt%EB%9E%80</guid>
            <pubDate>Mon, 18 Nov 2024 06:58:42 GMT</pubDate>
            <description><![CDATA[<p>백엔드 개발에서 가장 중요한 주제 중 하나는 사용자의 데이터를 안전하게 보호하며 인증과 인가를 처리하는 것입니다. 특히, 다양한 서비스 간의 데이터 공유가 필수가 된 환경에서는 안전하면서도 효율적인 인증/인가 방식이 필요합니다.
이 글에서는 <strong>JWT</strong>라는 핵심 기술을 살펴보고, 각각 백엔드 인증 시스템에서 어떤 역할을 하고 어떻게 협력하는지에 대해 알아봅니다. 여러분은 이 글을 통해 소셜 로그인과 같은 실제 사례에서 이 기술이 어떻게 사용되는지 이해하게 될 것입니다.</p>
<hr>
<h2 id="1-jwt-란">1. JWT 란?</h2>
<p>JWT는 JSON 형식의 데이터를 안전하게 주고받기 위해 사용하는 <strong>토큰 기반 인증 방식</strong>입니다. JWT는 주로 사용자의 인증 상태를 유지하거나 정보를 안전하게 전달하기 위해 사용됩니다. 이 토큰은 자체적으로 정보를 포함하고 있어, 별도의 서버 상태 저장 없이도 인증을 처리할 수 있다는 점이 큰 장점입니다.</p>
<hr>
<h3 id="1-1-jwt의-구조">1-1. JWT의 구조</h3>
<p>JWT는 <strong>.(점)</strong>으로 구분된 3개의 파트로 구성됩니다:</p>
<ul>
<li><strong>Header (헤더)</strong>: 토큰의 타입(JWT)과 서명 알고리즘 정보를 포함.<pre><code>{
&quot;alg&quot;: &quot;HS256&quot;,
&quot;typ&quot;: &quot;JWT&quot;
}</code></pre></li>
<li><strong>Payload (페이로드)</strong>: 사용자 정보 및 클레임(데이터)을 포함.<pre><code>{
&quot;sub&quot;: &quot;1234567890&quot;, // 사용자 ID
&quot;name&quot;: &quot;Federico15&quot;, // 사용자 이름
&quot;admin&quot;: true       // 관리자 여부
}</code></pre></li>
<li><strong>Signature (서명)</strong>: 헤더와 페이로드를 합쳐 비밀키로 서명한 값.</li>
</ul>
<p>서명은 토큰이 위조되지 않았음을 보장.</p>
<pre><code>HMACSHA256(
  base64UrlEncode(header) + &quot;.&quot; + base64UrlEncode(payload),
  secret
)</code></pre><hr>
<h3 id="1-2-jwt의-작동원리">1-2. JWT의 작동원리</h3>
<blockquote>
<ol>
<li>사용자가 <strong>로그인 요청</strong>을 보냄.</li>
<li>서버는 사용자를 <strong>인증</strong>하고, 사용자 정보를 담은 JWT를 클라이언트(브라우저나 앱)에 발급.</li>
<li>클라이언트는 이 JWT를 저장. (예: 로컬 스토리지, 쿠키).</li>
<li>이후 사용자가 서버에 요청을 보낼 때마다 JWT를 포함함.</li>
<li>서버는 받은 JWT를 확인하고 사용자를 인증. (서버는 상태를 저장할 필요가 없음).</li>
</ol>
</blockquote>
<hr>
<h3 id="1-3-jwt의-특징">1-3. JWT의 특징</h3>
<ol>
<li><p><strong>상태 비저장(Stateless)</strong>
서버는 JWT의 정보를 자체적으로 포함하고 있으므로, 세션 정보를 서버에 저장하지 않아도 됩니다.
서버 확장성(Scalability)이 높아지고, 분산 시스템에서 유용합니다.</p>
</li>
<li><p><strong>서명 기반 검증</strong>
서명을 통해 토큰이 변조되지 않았는지 확인할 수 있습니다.
비밀키(Secret Key)를 모르면 토큰의 서명을 생성할 수 없으므로, 위조를 방지할 수 있습니다.</p>
</li>
<li><p><strong>만료 시간 설정</strong>
exp 필드를 사용해 JWT의 유효 기간을 설정할 수 있습니다. 유효 시간이 지나면 더 이상 사용할 수 없도록 만료 처리가 됩니다.</p>
</li>
</ol>
<hr>
<h3 id="1-4-jwt의-장점">1-4. JWT의 장점</h3>
<blockquote>
</blockquote>
<ol>
<li><strong>확장성</strong>: 서버가 세션 상태를 저장하지 않으므로 대규모 서비스에 적합.</li>
<li><strong>독립성</strong>: JWT 자체에 필요한 정보가 담겨 있으므로 추가 요청 없이 인증 가능.</li>
<li><strong>유연성</strong>: JSON 형식이기 때문에 다양한 시스템과 호환 가능.</li>
</ol>
<hr>
<h3 id="1-5-jwt의-단점">1-5. JWT의 단점</h3>
<blockquote>
</blockquote>
<ol>
<li><strong>토큰 크기</strong>: JWT는 자체적으로 정보를 포함하므로, 일반적인 세션보다 크기가 큽니다.</li>
<li><strong>토큰 만료 전 무효화 어려움</strong>: JWT는 상태 비저장 방식이므로, 만료되기 전까지는 토큰을 무효화하기 어렵습니다.</li>
<li><strong>보안 위험</strong>: 토큰이 탈취되면, 만료 전까지 악의적으로 사용할 수 있습니다. 이를 방지하려면 HTTPS를 반드시 사용해야 합니다.</li>
</ol>
<hr>
<h3 id="1-6-jwt-사용사례">1-6. JWT 사용사례</h3>
<blockquote>
</blockquote>
<p><strong>API 인증</strong>: 사용자가 API를 호출할 때 JWT로 인증 상태를 확인.
<strong>싱글 사인온(SSO)</strong>: 여러 서비스에서 하나의 인증으로 로그인 상태를 유지.
<strong>모바일 앱 인증</strong>: 클라이언트-서버 간 통신에서 사용</p>
<hr>
<p>JWT는 <strong>인증(Authentication)</strong>을 위한 강력한 도구입니다. 서버는 인증 상태를 저장하지 않고도 클라이언트의 상태를 검증할 수 있으며, 확장성과 독립성이 뛰어나 대규모 분산 시스템에서도 효과적입니다. 그러나 JWT를 안전하게 사용하려면 HTTPS와 짧은 만료 시간, 그리고 토큰 재발급(Refresh Token) 전략을 반드시 고려해야 합니다.</p>
<p>다음포스트에서는 OAuth를 설명하며 JWT와의 관계를 연결할 수 있습니다. 다음 포스트에서 뵙겠습니다! 😊</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Java 에서 길이와 크기를 구하는 메서드들]]></title>
            <link>https://velog.io/@coding_goat/Java-Java-%EC%97%90%EC%84%9C-%EA%B8%B8%EC%9D%B4%EC%99%80-%ED%81%AC%EA%B8%B0%EB%A5%BC-%EA%B5%AC%ED%95%98%EB%8A%94-%EB%A9%94%EC%84%9C%EB%93%9C%EB%93%A4</link>
            <guid>https://velog.io/@coding_goat/Java-Java-%EC%97%90%EC%84%9C-%EA%B8%B8%EC%9D%B4%EC%99%80-%ED%81%AC%EA%B8%B0%EB%A5%BC-%EA%B5%AC%ED%95%98%EB%8A%94-%EB%A9%94%EC%84%9C%EB%93%9C%EB%93%A4</guid>
            <pubDate>Wed, 13 Nov 2024 15:23:46 GMT</pubDate>
            <description><![CDATA[<p>자바에는 문자열, 배열, 리스트 등 여러 자료형의 길이 또는 크기를 구할 수 있는 다양한 메서드들이 있습니다. 각 자료형에 따라 적합한 메서드를 선택하는 것이 중요합니다. 자주 쓰이는 메서드들을 정리해 보았습니다. 코딩테스트에서도 많이 활용되니, 익혀두길 바랍니다.</p>
<hr>
<h3 id="1-length">1. length</h3>
<p><code>length</code> - 배열의 길이</p>
<ul>
<li><p><strong>설명</strong>: 배열의 길이를 구할 때 사용되는 속성입니다. 메서드가 아닌 속성(length)으로, 괄호 없이 호출합니다.</p>
</li>
<li><p><strong>사용 예시:</strong></p>
<pre><code>int[] arr = {1, 2, 3};
System.out.println(arr.length); // 출력: 3</code></pre></li>
</ul>
<hr>
<h3 id="2-length">2. length()</h3>
<p><code>length()</code> - 문자열의 길이</p>
<ul>
<li><p><strong>설명</strong>: 문자열(String)의 길이를 구할 때 사용됩니다. 빈 문자열일 경우 0을 반환합니다.</p>
</li>
<li><p>** 사용 예시:**</p>
</li>
</ul>
<pre><code>String str = &quot;Hello&quot;;
System.out.println(str.length()); // 출력: 5</code></pre><p>문자열과 배열의 길이 구하는 메소드가 비슷하므로 헷갈리지 않아야합니다!</p>
<hr>
<h3 id="3-size">3. size()</h3>
<p><code>size()</code> - 컬렉션의 크기 (List, Set, Queue 등)</p>
<ul>
<li><p><strong>설명</strong>: <code>ArrayList</code>, <code>LinkedList</code>, <code>HashSet</code>, <code>Queue</code> 등 자바 컬렉션 프레임워크에서 요소의 개수를 구할 때 사용합니다.</p>
</li>
<li><p><strong>사용 예시</strong> :</p>
<pre><code>List&lt;String&gt; list = new ArrayList&lt;&gt;();
list.add(&quot;A&quot;);
list.add(&quot;B&quot;);
System.out.println(list.size()); // 출력: 2
</code></pre></li>
</ul>
<pre><code>---

### 4. count()

`count()` - 자바 스트림에서 요소 개수 구하기


- **설명**: 스트림(Streams)에서 특정 조건을 만족하는 요소의 개수를 셀 때 사용됩니다. 전체 개수를 세는 데에도 유용합니다.

- **사용 예시**:</code></pre><p>List<String> list = Arrays.asList(&quot;apple&quot;, &quot;banana&quot;, &quot;cherry&quot;);
long count = list.stream().filter(s -&gt; s.startsWith(&quot;a&quot;)).count(); // 출력: 1 (&quot;apple&quot;)</p>
<pre><code>---

** size()와 count()의 차이점 요약**

&gt; `size()`: 컬렉션에 포함된 전체 요소의 개수를 반환합니다. 스트림이 필요 없고, 컬렉션 클래스에서 바로 사용 가능합니다.

&gt; `count()`: 스트림에서 조건에 맞는 요소의 개수를 셀 때 주로 사용됩니다. 스트림을 통해서만 사용할 수 있고, filter()와 함께 조건부 개수 세기에 유용합니다.

두 메서드는 사용하는 대상과 필요 여부에 따라 다르게 선택하면 됩니다.

### 5. isEmpty()

`isEmpty()` - 비어 있는지 여부 확인

- **설명**: 리스트나 문자열, 맵 등이 비어 있는지 확인하는 메서드입니다. size() == 0을 대체하는 직관적인 방식입니다.

- **사용 예시** :</code></pre><p>List<String> list = new ArrayList&lt;&gt;();
System.out.println(list.isEmpty()); // 출력: true</p>
<pre><code>---

### 6. contains()

`contains()` - 특정 요소 포함 여부 확인

**설명**: `String`이나 `List`가 특정 문자 또는 요소를 포함하는지 확인할 때 사용합니다.

**사용 예시** :
</code></pre><p>String str = &quot;Hello World&quot;;
System.out.println(str.contains(&quot;World&quot;)); // 출력: true</p>
<h2 id="">```</h2>
<p>이러한 메서드와 클래스들은 코딩 테스트 문제를 빠르게 해결하고, 효율적인 코드를 작성하는 데 자주 사용됩니다. 다양한 문제를 풀어 보면서 각 메서드의 활용 방식을 익히면, 테스트에서 시간 단축과 코드 최적화에 큰 도움이 될 것 입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 스택(Stack)과 큐(Queue) ]]></title>
            <link>https://velog.io/@coding_goat/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%83%9DStack%EA%B3%BC-%ED%81%90Queue</link>
            <guid>https://velog.io/@coding_goat/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%83%9DStack%EA%B3%BC-%ED%81%90Queue</guid>
            <pubDate>Fri, 18 Oct 2024 08:24:06 GMT</pubDate>
            <description><![CDATA[<p>스택(Stack)과 큐(Queue)에 대해서 포스팅하겠습니다. 본 포스팅은 자바를 기반으로 설명합니다. </p>
<h1 id="1-스택stack이란">1. 스택(Stack)이란?</h1>
<hr>
<p>먼저, 스택을 형상화한 그림부터 보겠습니다.
<img src="https://velog.velcdn.com/images/coding_goat/post/84629146-d4a3-427d-b4bb-44ae4124209b/image.png" alt=""></p>
<p><code>스택(Stack)</code>은 <code>LIFO (Last In First Out)</code> 구조를 따르는 자료구조로, 마지막에 추가된 요소가 가장 먼저 제거되는 방식입니다. 즉, <strong>후입선출</strong> 방식으로 작동합니다. 쉽게 말해, 스택은 책을 쌓아 올리듯이 데이터를 쌓고, 가장 나중에 쌓은 책부터 꺼내는 것과 같은 개념입니다.</p>
<h2 id="1-1-스택의-기본연산">1-1. 스택의 기본연산</h2>
<hr>
<p>스택은 주로 두 가지 연산으로 이루어집니다:</p>
<ul>
<li><code>pop</code> : 스택의 맨 위에 새로운 데이터를 추가하는 연산</li>
<li><code>push</code> : 스택의 맨 위에 있는 데이터를 제거하는 연산</li>
</ul>
<p>이 외에도 스택의 상태를 확인하는 연산이 있습니다:</p>
<ul>
<li><code>peek</code> : 스택의 맨 위에 있는 데이터를 제거하지 않고 확인하는 연산</li>
<li><code>isEmpty</code> : 스택이 비어 있는지 확인하는 연산</li>
</ul>
<h2 id="1-2-스택의-동작-방식">1-2. 스택의 동작 방식</h2>
<hr>
<ol>
<li><code>push</code> 연산으로 스택에 데이터를 쌓습니다.</li>
<li><code>pop</code> 연산으로 스택에서 가장 최근에 추가된 데이터를 제거합니다.</li>
</ol>
<h2 id="1-3-스택의-활용-예시">1-3. 스택의 활용 예시</h2>
<hr>
<ul>
<li><p>함수 호출: 함수 호출 시, 호출된 함수의 정보를 스택에 저장하고, 함수가 종료되면 해당 정보가 스택에서 제거됩니다. 이를 <strong>호출 스택(Call Stack)</strong>이라고 합니다.</p>
</li>
<li><p>괄호 검사: 중첩된 괄호의 짝이 맞는지 검사할 때 스택을 사용하여 괄호의 쌍을 확인할 수 있습니다.</p>
</li>
<li><p>백트래킹(Backtracking): DFS(깊이 우선 탐색)와 같은 알고리즘에서 스택을 사용하여 재귀적 탐색을 수행할 수 있습니다.</p>
</li>
</ul>
<h2 id="1-4-스택의-예시-코드-자바">1-4. 스택의 예시 코드 (자바)</h2>
<hr>
<pre><code>import java.util.Stack;

public class StackExample {
    public static void main(String[] args) {
        Stack&lt;Integer&gt; stack = new Stack&lt;&gt;();

        // 스택에 요소 추가 (push)
        stack.push(10);
        stack.push(20);
        stack.push(30);

        // 스택에서 가장 위의 요소 확인 (peek)
        System.out.println(&quot;Top element: &quot; + stack.peek());  // 출력: 30

        // 스택에서 가장 위의 요소 제거 (pop)
        System.out.println(&quot;Popped element: &quot; + stack.pop());  // 출력: 30
        System.out.println(&quot;Popped element: &quot; + stack.pop());  // 출력: 20

        // 스택의 남은 요소
        System.out.println(&quot;Remaining elements: &quot; + stack);  // 출력: [10]
    }
}
</code></pre><p>위 코드를 통해 <code>push</code>, <code>pop</code>, <code>peek</code> 연산이 어떻게 동작하는지 확인할 수 있습니다.</p>
<p>스택은 데이터가 쌓였다가 제거되는 순서가 중요한 경우에 매우 유용하며, 다양한 알고리즘과 문제 해결에 자주 활용됩니다.</p>
<h1 id="2-큐queue-란">2. 큐(Queue) 란?</h1>
<hr>
<p>먼저, 큐를 형상화한 그림부터 보겠습니다.
<img src="https://velog.velcdn.com/images/coding_goat/post/35018bda-2dfa-4c21-8bff-dc7be07c362e/image.png" alt=""></p>
<p><code>큐(Queue)</code>는 <code>FIFO (First In First Out)</code> 구조를 따르는 자료구조로, 먼저 추가된 데이터가 먼저 제거되는 방식입니다. 즉, 선입선출 방식으로 작동합니다. 줄을 서는 상황을 생각하면 쉽게 이해할 수 있는데, 먼저 줄을 선 사람이 먼저 처리되는 것과 같은 개념입니다.</p>
<h2 id="2-1-큐의-기본연산">2-1. 큐의 기본연산</h2>
<hr>
<p>큐는 주로 세 가지 연산으로 이루어집니다:</p>
<ul>
<li><code>offer</code>: 큐의 끝에 새로운 데이터를 추가하는 연산 (또는 add).</li>
<li><code>poll</code>: 큐의 앞에 있는 데이터를 제거하는 연산 (또는 remove).</li>
<li><code>peek</code>: 큐의 앞에 있는 데이터를 제거하지 않고 확인하는 연산.</li>
</ul>
<p>이 외에도 큐의 상태를 확인하는 연산이 있습니다:</p>
<ul>
<li><code>isEmpty</code>: 큐가 비어 있는지 확인하는 연산.</li>
</ul>
<h2 id="2-2-큐의-동작-방식">2-2. 큐의 동작 방식</h2>
<hr>
<ol>
<li><code>offer</code> 연산으로 큐의 끝에 데이터를 추가합니다.</li>
<li><code>poll</code> 연산으로 큐의 앞에 있는 데이터를 제거합니다.</li>
</ol>
<h2 id="2-3-큐의-활용-예시">2-3. 큐의 활용 예시</h2>
<ul>
<li><p><strong>프린터 대기열</strong>: 프린터는 먼저 들어온 작업부터 순차적으로 처리하기 때문에 큐 구조로 관리됩니다.</p>
</li>
<li><p><strong>프로세스 관리</strong>: 운영 체제에서 프로세스를 처리할 때, 먼저 들어온 작업부터 처리하는 구조로 큐를 사용합니다.</p>
</li>
<li><p><strong>BFS (너비 우선 탐색)</strong>: 그래프 탐색 알고리즘에서 큐를 사용하여 한 레벨씩 차례대로 탐색할 수 있습니다.</p>
</li>
</ul>
<h2 id="2-4-큐의-예시-코드-자바">2-4. 큐의 예시 코드 (자바)</h2>
<pre><code>import java.util.LinkedList;
import java.util.Queue;

public class QueueExample {
    public static void main(String[] args) {
        Queue&lt;Integer&gt; queue = new LinkedList&lt;&gt;();

        // 큐에 요소 추가 (offer)
        queue.offer(10);
        queue.offer(20);
        queue.offer(30);

        // 큐에서 맨 앞의 요소 확인 (peek)
        System.out.println(&quot;Front element: &quot; + queue.peek());  // 출력: 10

        // 큐에서 맨 앞의 요소 제거 (poll)
        System.out.println(&quot;Polled element: &quot; + queue.poll());  // 출력: 10
        System.out.println(&quot;Polled element: &quot; + queue.poll());  // 출력: 20

        // 큐의 남은 요소
        System.out.println(&quot;Remaining elements: &quot; + queue);  // 출력: [30]
    }
}</code></pre><p>위 코드를 통해 큐에서 <code>offer</code>, <code>poll</code>, <code>peek</code> 연산이 어떻게 동작하는지 확인할 수 있습니다. <code>offer</code>로 데이터를 추가하고, <code>poll</code>로 데이터를 제거하며, <code>peek</code>으로 맨 앞에 있는 데이터를 확인할 수 있습니다.</p>
<p>큐는 데이터가 처리된 순서가 중요한 경우에 많이 사용되며, 대기열이나 너비 우선 탐색(BFS) 등에서 자주 활용되는 자료구조입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 부트 입문: 정적 컨텐츠 제공, MVC, RESTful API 구현까지 3 (RESTful API)]]></title>
            <link>https://velog.io/@coding_goat/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EC%9E%85%EB%AC%B8-%EC%A0%95%EC%A0%81-%EC%BB%A8%ED%85%90%EC%B8%A0-%EC%A0%9C%EA%B3%B5-MVC-RESTful-API-%EA%B5%AC%ED%98%84%EA%B9%8C%EC%A7%80-3-RESTful-API</link>
            <guid>https://velog.io/@coding_goat/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EC%9E%85%EB%AC%B8-%EC%A0%95%EC%A0%81-%EC%BB%A8%ED%85%90%EC%B8%A0-%EC%A0%9C%EA%B3%B5-MVC-RESTful-API-%EA%B5%AC%ED%98%84%EA%B9%8C%EC%A7%80-3-RESTful-API</guid>
            <pubDate>Thu, 12 Sep 2024 06:19:32 GMT</pubDate>
            <description><![CDATA[<p>현대적인 웹 애플리케이션 개발에서 API(Application Programming Interface)는 클라이언트와 서버 간의 데이터 통신을 가능하게 하는 핵심적인 요소입니다. 특히 RESTful API는 웹 서비스의 표준으로 자리 잡으면서, 간결하고 직관적인 방식으로 데이터를 주고받을 수 있는 강력한 방법론을 제공합니다.</p>
<p>REST(Representational State Transfer)는 웹의 리소스를 HTTP 메서드(GET, POST, PUT, DELETE 등)를 통해 처리하는 아키텍처 스타일입니다. 이 방식은 애플리케이션이 서로 독립적으로 동작하면서도 데이터를 쉽게 교환할 수 있게 해줍니다. 예를 들어, 프론트엔드 애플리케이션, 모바일 앱, 심지어 외부 서비스도 RESTful API를 사용하여 서버와 통신할 수 있습니다.</p>
<p>이번 포스트에서는 RESTful API의 기본 개념을 설명하고, 스프링 부트를 사용해 실제로 RESTful API를 구축하는 방법을 살펴보겠습니다. 스프링 부트의 강력한 기능을 통해 간단한 API를 쉽게 만들고, JSON 형식으로 데이터를 주고받는 과정을 단계별로 알아볼 예정입니다.</p>
<h2 id="1-restful-api의-기본-개념">1. RESTful API의 기본 개념</h2>
<p>RESTful API는 REST(Representational State Transfer) 아키텍처 스타일을 기반으로 클라이언트와 서버가 데이터를 주고받는 방식입니다. RESTful API는 웹 상에서 리소스를 효율적으로 관리하고, HTTP 프로토콜을 기반으로 동작하는 특징이 있습니다. 여기서는 RESTful API의 핵심 개념과 원칙에 대해 설명합니다.</p>
<h3 id="1-1-리소스-기반-설계">1-1. 리소스 기반 설계</h3>
<p>REST는 모든 것을 <strong>리소스(Resource)</strong>로 간주합니다. 리소스는 서버가 관리하는 데이터를 의미하며, 각 리소스는 고유한 URI(Uniform Resource Identifier)를 통해 식별됩니다. RESTful API는 특정 리소스에 접근할 수 있는 경로(엔드포인트)를 제공합니다.</p>
<p>예를 들어,</p>
<blockquote>
<ul>
<li><code>GET /users</code> : 모든 사용자 정보를 가져오는 요청</li>
</ul>
</blockquote>
<ul>
<li><code>GET /users/{id}</code> : 특정 ID를 가진 사용자의 정보를 가져오는 요청</li>
<li><code>POST /users</code> : 새로운 사용자를 생성하는 요청</li>
<li><code>PUT /users/{id}</code> : 특정 ID의 사용자의 정보를 업데이트하는 요청</li>
<li><code>DELETE /users/{id}</code> : 특정 ID의 사용자를 삭제하는 요청</li>
</ul>
<p>이와 같이, RESTful API에서는 URI를 통해 리소스를 명확하게 식별하고 관리합니다.</p>
<h3 id="1-2-무상태성-statelessness">1-2. 무상태성 (Statelessness)</h3>
<p>RESTful API는 무상태성(stateless) 원칙을 따릅니다. 즉, 서버는 클라이언트의 이전 요청에 대한 상태를 저장하지 않습니다. 각 요청은 독립적으로 처리되며, 요청 간의 상태를 유지하지 않으므로 서버의 부담을 덜어줍니다. 클라이언트는 필요한 모든 정보를 요청에 포함시켜야 하며, 서버는 해당 요청에 따라 바로 응답을 줍니다.</p>
<p>예를 들어, 클라이언트가 로그인 상태를 유지하기 위해 매번 인증 토큰을 요청 헤더에 포함시켜야 합니다. 서버는 이 토큰을 통해 클라이언트를 식별하지만, 이전 요청의 상태를 기억하지는 않습니다.</p>
<h3 id="1-3-http-메소드">1-3. HTTP 메소드</h3>
<p>RESTful API는 <strong>HTTP 메소드</strong>를 사용하여 리소스에 대한 작업을 정의합니다. 각 메서드는 특정한 목적을 가지고 있으며, 요청을 보낼 때 어떤 작업을 하고 싶은지를 명확하게 나타냅니다.</p>
<p>다음은 주요 HTTP메소드와 그 역할입니다.</p>
<blockquote>
<ul>
<li>GET : 서버에서 리소스를 조회합니다. 데이터를 가져오기 위한 요청이며, 서버의 상태를 변경하지 않습니다.</li>
</ul>
</blockquote>
<ul>
<li>POST : 서버에 새로운 리소스를 생성합니다. 예를 들어, 새로운 사용자 정보를 서버에 추가할 때 사용합니다.</li>
<li>PUT : 서버에서 기존 리소스를 업데이트합니다. 전체 데이터를 수정할 때 사용되며, 클라이언트는 수정된 전체 데이터를 전달해야 합니다.</li>
<li>PATCH : 기존 리소스의 일부만 업데이트합니다. PUT과 달리 전체 데이터가 아닌, 일부 데이터만 수정합니다.</li>
<li>DELETE : 서버에서 리소스를 삭제합니다. </li>
</ul>
<h3 id="1-4-클라이언트-서버-간의-역할-분리">1-4. 클라이언트-서버 간의 역할 분리</h3>
<p>RESTful 아키텍처에서는 <strong>클라이언트</strong>와 <strong>서버</strong>가 명확하게 분리됩니다. 클라이언트는 사용자 인터페이스(UI)와 관련된 작업을 처리하고, 서버는 데이터 저장 및 비즈니스 로직을 처리하는 역할을 맡습니다. 클라이언트와 서버는 API를 통해 데이터를 주고받으며, 클라이언트는 서버의 내부 구조를 알 필요가 없습니다.</p>
<p>이러한 분리는 애플리케이션의 유연성을 높이고, 클라이언트와 서버의 독립적인 발전을 가능하게 합니다. 예를 들어, 웹 애플리케이션의 UI가 변경되어도 서버의 데이터 처리 로직은 변경할 필요가 없습니다.</p>
<h3 id="1-5-표현의-일관성-uniform-interface">1-5. 표현의 일관성 (Uniform Interface)</h3>
<p>RESTful API는 <strong>일관된 인터페이스</strong>를 제공합니다. 즉, 동일한 URI와 메서드를 사용하여 클라이언트는 일관되게 서버에 접근할 수 있습니다. 이러한 일관성은 개발자가 API를 더 쉽게 이해하고 사용할 수 있게 합니다.</p>
<p>예를 들어, <code>/users</code>라는 경로는 언제나 사용자 리소스를 의미하고, <code>GET /users</code>는 항상 사용자 목록을 반환합니다. 이러한 일관성 덕분에 RESTful API는 예측 가능하고 쉽게 사용할 수 있습니다.</p>
<h3 id="1-6-json-형식으로-데이터-교환">1-6. JSON 형식으로 데이터 교환</h3>
<p>RESTful API에서는 데이터 교환을 위해 JSON형식을 주로 사용합니다. JSON은 경량의 데이터 포맷으로, 서버와 클라이언트 간에 데이터를 쉽게 주고받을 수 있게 해줍니다. JSON은 간결하면서도 구조화된 데이터를 표현할 수 있어, RESTful API와 잘 맞습니다.</p>
<pre><code>{
  &quot;id&quot;: 1,
  &quot;name&quot;: &quot;Federico&quot;,
  &quot;email&quot;: &quot;Federico@example.com&quot;
}</code></pre><p>이와 같이, RESTful API는 리소스 기반 설계, 무상태성, HTTP 메서드를 활용한 일관된 인터페이스를 통해 클라이언트-서버 간의 효율적인 데이터 통신을 가능하게 합니다. 이제 이러한 개념을 바탕으로, 스프링 부트를 사용하여 실제로 RESTful API를 구축하는 방법을 알아보겠습니다.</p>
<h2 id="2스프링-부트에서-restful-api-구축">2.스프링 부트에서 RESTful API 구축</h2>
<p>스프링 부트는 RESTful API를 쉽게 구현할 수 있는 다양한 기능을 제공합니다. RESTful API를 구축할 때 주로 사용하는 어노테이션들과 HTTP 메서드를 처리하는 방법을 알아보겠습니다.</p>
<h3 id="2-1-restcontroller-와-requestmapping">2-1. @RestController 와 @RequestMapping</h3>
<blockquote>
<ul>
<li><code>@RestController</code> : 스프링 부트에서 RESTful API를 만들기 위해서는 @RestController 어노테이션을 사용합니다. 이는 해당 클래스가 REST API 요청을 처리하는 컨트롤러임을 나타내며, JSON 형식으로 데이터를 반환할 수 있습니다.</li>
</ul>
</blockquote>
<ul>
<li><code>@RequestMapping</code> : API 엔드포인트를 정의할 때 사용되는 어노테이션으로, 특정 URL 경로와 HTTP 메서드를 매핑합니다. 이를 통해 API 요청을 처리할 수 있습니다.</li>
</ul>
<pre><code>@RestController
@RequestMapping(&quot;/api/users&quot;) // 이 클래스는 /api/users로 들어오는 요청을 처리
public class UserController {

    @GetMapping
    public List&lt;User&gt; getUsers() {
        // 모든 사용자 목록을 반환하는 로직
        return userService.findAllUsers();
    }

    @PostMapping
    public User createUser(@RequestBody User user) {
        // 새로운 사용자 생성 로직
        return userService.saveUser(user);
    }

    @PutMapping(&quot;/{id}&quot;)
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        // 사용자 정보 수정 로직
        return userService.updateUser(id, user);
    }

    @DeleteMapping(&quot;/{id}&quot;)
    public void deleteUser(@PathVariable Long id) {
        // 사용자 삭제 로직
        userService.deleteUser(id);
    }
}</code></pre><p>이 예제에서는 <code>/api/users</code> 경로에 매핑된 여러 HTTP 메서드(GET, POST, PUT, DELETE)를 처리하는 엔드포인트를 정의하고 있습니다. 이를 통해 사용자 목록 조회, 사용자 생성, 수정, 삭제와 같은 작업을 수행할 수 있습니다.</p>
<h3 id="2-2-http-메소드-매핑">2-2. HTTP 메소드 매핑</h3>
<p>스프링 부트에서는 HTTP 메소드에 따라 요청을 처리할 수 있습니다. 주로 사용하는 어노테이션은 다음과 같습니다.</p>
<blockquote>
<ul>
<li><code>@GetMapping</code>: 데이터를 조회하는 GET 요청을 처리합니다.</li>
</ul>
</blockquote>
<ul>
<li><code>@PostMapping</code>: 새로운 데이터를 생성하는 POST 요청을 처리합니다.</li>
<li><code>@PutMapping</code>: 기존 데이터를 수정하는 PUT 요청을 처리합니다.</li>
<li><code>@DeleteMapping</code>: 데이터를 삭제하는 DELETE 요청을 처리합니다.
각 메서드는 엔드포인트를 설정하고, 요청 데이터를 처리하거나, 서버에서 데이터를 반환하는 역할을 합니다.</li>
</ul>
<h3 id="2-3-requestbody와-pathvariable-사용">2-3. @RequestBody와 @PathVariable 사용</h3>
<blockquote>
<ul>
<li><code>@RequestBody</code>: 클라이언트에서 보내는 JSON 데이터를 객체로 변환합니다. 주로 POST나 PUT 요청에서 데이터를 서버로 전송할 때 사용됩니다.</li>
</ul>
</blockquote>
<ul>
<li><code>@PathVariable</code>: URL 경로에 포함된 변수 값을 받아옵니다. 예를 들어, /users/1에서 1이라는 값을 받아오는 데 사용됩니다.</li>
</ul>
<pre><code>@PostMapping(&quot;/create&quot;)
public User createUser(@RequestBody User user) {
    return userService.saveUser(user); // RequestBody로 전달된 JSON 데이터를 User 객체로 변환
}

@PutMapping(&quot;/{id}&quot;)
public User updateUser(@PathVariable Long id, @RequestBody User user) {
    return userService.updateUser(id, user); // 경로의 id를 받아와서 해당 사용자 업데이트
}</code></pre><p>이제 RESTful API를 스프링 부트로 구축하는 방법을 알았으니, 이를 바탕으로 간단한 CRUD API 예제를 만들어 보겠습니다.</p>
<h2 id="3간단한-crud-api-예제">3.간단한 CRUD API 예제</h2>
<p>이제 사용자 정보를 관리하는 간단한 CRUD(Create, Read, Update, Delete) API를 만들어 보겠습니다. 이 API는 사용자 목록을 조회하고, 새로운 사용자를 추가하고, 사용자의 정보를 수정하고, 삭제하는 기능을 포함합니다.</p>
<h3 id="3-1-사용자-엔티티-생성">3-1. 사용자 엔티티 생성</h3>
<p>먼저, 사용자 정보를 저장하는 User 클래스를 정의합니다. 이 클래스는 데이터베이스에 저장되는 사용자 정보를 담고 있습니다.</p>
<pre><code>@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String email;

    // Getter, Setter, 생성자 등 생략
}
</code></pre><h3 id="3-2-사용자-리포지토리-인터페이스">3-2. 사용자 리포지토리 인터페이스</h3>
<p>다음으로, 사용자 데이터를 처리할 <code>UserRepository</code> 인터페이스를 정의합니다. 이는 데이터베이스와 상호작용하며, 기본적인 CRUD 작업을 지원합니다.</p>
<pre><code>public interface UserRepository extends JpaRepository&lt;User, Long&gt; {
}</code></pre><h3 id="3-3-사용자-서비스-로직">3-3. 사용자 서비스 로직</h3>
<p>사용자 데이터를 처리하는 비즈니스 로직을 포함한 <code>UserService</code> 클래스를 작성합니다. 여기서는 사용자 목록 조회, 생성, 수정, 삭제 등의 작업을 정의합니다.</p>
<pre><code>@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public List&lt;User&gt; findAllUsers() {
        return userRepository.findAll();
    }

    public User saveUser(User user) {
        return userRepository.save(user);
    }

    public User updateUser(Long id, User updatedUser) {
        User existingUser = userRepository.findById(id).orElseThrow();
        existingUser.setName(updatedUser.getName());
        existingUser.setEmail(updatedUser.getEmail());
        return userRepository.save(existingUser);
    }

    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
}
</code></pre><h3 id="3-4-사용자-컨트롤러-정의">3-4. 사용자 컨트롤러 정의</h3>
<p>마지막으로, 사용자 요청을 처리할 <code>UserController</code> 클래스를 정의합니다. 이 컨트롤러는 API 엔드포인트를 통해 사용자 정보를 조회, 추가, 수정, 삭제하는 역할을 합니다.</p>
<pre><code>@RestController
@RequestMapping(&quot;/api/users&quot;)
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping
    public List&lt;User&gt; getAllUsers() {
        return userService.findAllUsers();
    }

    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.saveUser(user);
    }

    @PutMapping(&quot;/{id}&quot;)
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        return userService.updateUser(id, user);
    }

    @DeleteMapping(&quot;/{id}&quot;)
    public void deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }
}
</code></pre><p>이번 포스트에서는 스프링 부트를 사용하여 RESTful API를 구축하는 방법을 알아보고, 간단한 CRUD API 예제를 통해 사용자 데이터를 관리하는 과정을 살펴보았습니다. RESTful API는 현대적인 애플리케이션 개발에서 필수적인 요소로, 클라이언트와 서버 간에 데이터를 효율적으로 주고받을 수 있게 해줍니다.</p>
<p>특히 스프링 부트는 RESTful API를 쉽게 구현할 수 있는 다양한 기능을 제공하므로, 이를 잘 활용하면 확장성과 유지 보수성이 뛰어난 애플리케이션을 만들 수 있습니다. 이번 예제를 바탕으로 더 복잡한 API를 설계하고, 인증, 유효성 검사, 오류 처리와 같은 기능을 추가해 보세요.</p>
<p>RESTful API는 다양한 플랫폼에서 데이터를 주고받는 표준 방식이기 때문에, 이를 확실하게 이해하고 구현하는 것은 개발자로서 중요한 역량입니다.</p>
<p>다음 포스트에서 뵙겠습니다~!</p>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/948aa7b2-fbe4-42c5-9457-523c6977956b/image.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 부트 입문: 정적 컨텐츠 제공, MVC, RESTful API 구현까지 2 (MVC 패턴)]]></title>
            <link>https://velog.io/@coding_goat/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EC%9E%85%EB%AC%B8-%EC%A0%95%EC%A0%81-%EC%BB%A8%ED%85%90%EC%B8%A0-%EC%A0%9C%EA%B3%B5-MVC-RESTful-API-%EA%B5%AC%ED%98%84%EA%B9%8C%EC%A7%80-2-MVC-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@coding_goat/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EC%9E%85%EB%AC%B8-%EC%A0%95%EC%A0%81-%EC%BB%A8%ED%85%90%EC%B8%A0-%EC%A0%9C%EA%B3%B5-MVC-RESTful-API-%EA%B5%AC%ED%98%84%EA%B9%8C%EC%A7%80-2-MVC-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Tue, 20 Aug 2024 00:14:34 GMT</pubDate>
            <description><![CDATA[<p>현대적인 웹 애플리케이션 개발에서 <strong>MVC(Model-View-Controller) 패턴</strong>은 가장 널리 사용되는 설계 패턴 중 하나입니다. 이 패턴은 애플리케이션을 세 가지 주요 컴포넌트인 <strong>모델(Model)</strong>, <strong>뷰(View)</strong>, 그리고 <strong>컨트롤러(Controller)</strong>로 분리하여, 코드의 유지보수성과 확장성을 크게 향상시킵니다.</p>
<p>스프링 부트(Spring Boot)는 이러한 MVC 패턴을 쉽게 적용할 수 있도록 지원하며, 이를 통해 개발자는 복잡한 웹 애플리케이션을 체계적이고 효율적으로 설계할 수 있습니다. MVC 패턴은 사용자 인터페이스와 비즈니스 로직을 명확히 구분함으로써, 웹 애플리케이션의 구조를 깔끔하게 유지하고, 개발과 테스트를 더욱 용이하게 합니다.</p>
<p>이번 포스트에서는 스프링 부트에서 <strong>MVC 패턴</strong>을 어떻게 구현할 수 있는지 살펴보겠습니다. <strong>모델(Model)</strong>이 데이터와 비즈니스 로직을 어떻게 관리하고, <strong>컨트롤러(Controller)</strong>가 사용자 요청을 처리하며, <strong>뷰(View)</strong>가 사용자에게 정보를 어떻게 제공하는지를 단계별로 알아볼 것입니다. 또한, 간단한 예제를 통해 MVC 패턴에 대해서 알아보겠습니다.</p>
<h2 id="1-mvc-패턴의-정의">1. MVC 패턴의 정의</h2>
<blockquote>
<p><strong>MVC (모델-뷰-컨트롤러)</strong> 는 사용자 인터페이스, 데이터 및 논리 제어를 구현하는데 널리 사용되는 소프트웨어 디자인 패턴입니다. 소프트웨어의 비즈니스 로직과 화면을 구분하는데 중점을 두고 있습니다. 이러한 &quot;관심사 분리&quot; 는 더나은 업무의 분리와 향상된 관리를 제공합니다.</p>
</blockquote>
<h3 id="1-1-모델model의-정의와-예시">1-1) 모델(Model)의 정의와 예시</h3>
<blockquote>
<p><strong>모델(Model)</strong>은 애플리케이션의 데이터와 비즈니스 로직을 관리하는 구성 요소입니다. 모델은 데이터를 저장, 수정, 삭제하는 책임을 가지며, 데이터베이스와의 상호작용을 통해 애플리케이션의 상태를 유지합니다. 또한, 비즈니스 로직을 처리하고, 뷰(View)나 컨트롤러(Controller)와의 데이터 교환을 담당합니다.</p>
</blockquote>
<blockquote>
<p>예시 : 스프링 부트에서는 일반적으로 @Entity 어노테이션이 적용된 클래스가 모델을 나타내며, 데이터베이스와 연동되는 리포지토리(Repository) 클래스(@Repository)와 서비스(Service) 클래스(@Service)가 모델의 주요 부분을 구성합니다.</p>
</blockquote>
<h3 id="1-2-뷰view의-정의와-예시">1-2) 뷰(View)의 정의와 예시</h3>
<blockquote>
<p><strong>뷰(View)</strong>는 사용자가 실제로 보는 화면, 즉 사용자 인터페이스(UI)를 담당하는 구성 요소입니다. 뷰는 모델이 제공하는 데이터를 사용자에게 표시하며, 사용자로부터 입력을 받습니다. 그러나 뷰는 데이터나 비즈니스 로직에 직접적인 접근을 하지 않으며, 단순히 데이터를 표시하고 사용자 입력을 전달하는 역할만 합니다.</p>
</blockquote>
<blockquote>
<p>예시 : 스프링 부트에서 뷰는 주로 템플릿 엔진(Thymeleaf, Mustache 등)을 사용하여 구현됩니다. 뷰 템플릿은 HTML 파일의 형태로 작성되며, 컨트롤러가 전달한 데이터를 받아 화면에 출력합니다. (오늘 포스트 할 예제는 Thymeleaf를 사용할 예정입니다!)</p>
</blockquote>
<h3 id="1-3-컨트롤러controller의-정의와-예시">1-3) 컨트롤러(Controller)의 정의와 예시</h3>
<blockquote>
<p><strong>컨트롤러(Controller)</strong>는 사용자의 요청을 처리하고, 그에 따라 모델을 업데이트하며, 적절한 뷰를 선택해 데이터를 전달하는 역할을 담당합니다. 컨트롤러는 사용자가 웹 애플리케이션에서 어떤 동작을 요청했는지 분석하고, 그에 맞는 비즈니스 로직을 실행하거나, 모델의 데이터를 업데이트합니다. 이후, 해당 결과를 뷰에 전달하여 사용자에게 보여지도록 합니다.</p>
</blockquote>
<blockquote>
<p>예시: 스프링 부트에서는 @Controller 어노테이션이 적용된 클래스가 컨트롤러 역할을 수행하며, @RequestMapping, @GetMapping, @PostMapping 등의 어노테이션을 통해 특정 URL 요청을 처리하는 메서드를 정의합니다.</p>
</blockquote>
<h2 id="2-mvc-패턴의-간단한-예제">2. MVC 패턴의 간단한 예제</h2>
<pre><code>// controller

@Controller
 public class HelloController {
    @GetMapping(&quot;hello-mvc&quot;)
 public String helloMvc(@RequestParam(&quot;name&quot;) String name, Model model) {
        model.addAttribute(&quot;name&quot;, name);
 return &quot;hello-template&quot;;
    }
 }</code></pre><pre><code>// view
// resources/templates/hello-template.html

&lt;html xmlns:th=&quot;http://www.thymeleaf.org&quot;&gt;
 &lt;body&gt;
 &lt;p th:text=&quot;&#39;hello &#39; + ${name}&quot;&gt;hello! empty&lt;/p&gt;
 &lt;/body&gt;
 &lt;/html&gt;</code></pre><p><img src="https://velog.velcdn.com/images/coding_goat/post/d752d275-0eb5-492e-9e55-3c292c5b820b/image.png" alt=""></p>
<h3 id="mvc-패턴의-작동원리">MVC 패턴의 작동원리</h3>
<p>위의 예제에 대한 작동원리를 알아보기 전, MVC의 작동원리에 대해서 간단하게 설명하고, 위의 예제를 적용해보겠습니다. </p>
<blockquote>
</blockquote>
<p>** 1. 사용자 요청(User Request)**</p>
<ul>
<li>사용자가 웹 브라우저를 통해 특정 URL에 접근하거나, 버튼 클릭, 폼 제출 등의 동작을 수행합니다. 이때 HTTP 요청이 서버로 전송됩니다.</li>
</ul>
<blockquote>
<p>** 2. 컨트롤러가 요청을 처리 (Controller Handles the Request)**</p>
</blockquote>
<ul>
<li>서버로 전송된 요청은 먼저 컨트롤러(Controller)에게 전달됩니다. 스프링 부트에서는 이 역할을 @Controller 어노테이션이 붙은 클래스가 담당합니다.</li>
<li>컨트롤러는 요청된 URL과 매핑된 메서드를 실행합니다. 예를 들어, 사용자가 /hello URL로 요청을 보냈다면, 해당 URL에 매핑된 메서드가 실행됩니다.</li>
</ul>
<blockquote>
<p><strong>3. 비즈니스 로직 수행 및 데이터 처리 (Business Logic and Data Processing)</strong></p>
</blockquote>
<ul>
<li>컨트롤러는 요청을 처리하기 위해 필요한 경우 모델(Model)과 상호작용합니다.</li>
<li>모델은 데이터베이스에서 데이터를 가져오거나, 비즈니스 로직을 처리합니다. 이 과정에서 데이터의 생성, 수정, 삭제 등의 작업이 수행될 수 있습니다.</li>
<li>예를 들어, 사용자가 제품 목록을 요청했다면, 모델은 데이터베이스에서 제품 데이터를 가져오게 됩니다.</li>
</ul>
<blockquote>
<p><strong>4.모델 데이터를 컨트롤러로 반환 (Model Data Returned to Controller)</strong></p>
</blockquote>
<ul>
<li>모델이 처리한 데이터는 컨트롤러로 반환됩니다. 컨트롤러는 이 데이터를 뷰(View)에 전달할 준비를 합니다.</li>
</ul>
<blockquote>
<p><strong>5. 뷰 선택 및 데이터 전달 (View Selection and Data Passing)</strong></p>
</blockquote>
<ul>
<li>컨트롤러는 반환된 데이터를 적절한 뷰(View)에 전달합니다. 스프링 부트에서는 주로 템플릿 엔진(Thymeleaf, Mustache 등)을 사용해 뷰를 생성합니다.</li>
<li>컨트롤러는 뷰의 이름과 함께 모델 데이터를 넘겨주어, 해당 데이터를 화면에 표시하도록 합니다.</li>
<li>예를 들어, 컨트롤러가 &quot;productList&quot;라는 뷰 이름을 반환하면, 스프링은 &quot;productList.html&quot; 템플릿 파일을 찾아 데이터를 그 안에 렌더링합니다.</li>
</ul>
<blockquote>
<p><strong>6. 뷰가 사용자에게 렌더링 (View Rendered to User)</strong></p>
</blockquote>
<ul>
<li>뷰는 전달받은 데이터를 HTML로 렌더링하여 사용자에게 보이는 웹 페이지를 생성합니다.</li>
<li>사용자는 최종적으로 이 HTML 페이지를 웹 브라우저에서 확인하게 됩니다.</li>
</ul>
<h3 id="예제-작동원리">예제 작동원리</h3>
<blockquote>
<ol>
<li>사용자가 /hello-mvc?name=John URL로 요청을 보냅니다.</li>
<li>스프링 컨트롤러가 이 요청을 받아 name 파라미터를 추출하고, 그 값을 모델에 추가합니다.</li>
<li>컨트롤러는 hello-template이라는 이름의 뷰 템플릿을 반환합니다.</li>
<li>템플릿 엔진(Thymeleaf)은 hello-template.html 파일을 렌더링하면서, model에서 전달된 name 값을 사용해 &quot;hello John&quot;이라는 내용을 생성합니다.</li>
<li>최종적으로, 생성된 HTML이 사용자에게 전달되고, 사용자는 웹 페이지에서 &quot;hello John&quot;이라는 문구를 확인합니다.</li>
</ol>
</blockquote>
<p>이 과정에서 MVC 패턴의 각 요소가 명확히 분리되어 동작하며, 요청 처리, 비즈니스 로직 실행, 그리고 화면 렌더링의 역할이 각각 구분되어 있습니다.</p>
<p>이번 포스트에서는 스프링 부트에서 MVC 패턴을 활용해 간단한 웹 애플리케이션을 구성하는 방법을 살펴보았습니다. 컨트롤러, 모델, 뷰가 각각 어떻게 동작하며, 사용자 요청이 처리되고 결과가 화면에 표시되는지 구체적인 예제를 통해 이해할 수 있었습니다. MVC 패턴은 웹 애플리케이션의 구조를 깔끔하고 유지보수하기 쉽게 만들어주므로, 이를 잘 이해하고 활용하는 것은 매우 중요합니다.</p>
<p>이제 스프링 부트를 사용해 보다 복잡한 애플리케이션을 만들 때, MVC 패턴을 활용하여 더욱 구조적이고 효율적인 코드를 작성할 수 있을 것입니다. 다음 포스트에서는 다음 포스트에서는 스프링 부트에서 API(Application Programming Interface)를 구축하는 방법에 대해 다룰 예정입니다. 다음 포스트에서 만나요~!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 부트 입문: 정적 컨텐츠 제공, MVC, RESTful API 구현까지 1 (정적 컨텐츠)]]></title>
            <link>https://velog.io/@coding_goat/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EC%9E%85%EB%AC%B8-%EC%A0%95%EC%A0%81-%EC%BB%A8%ED%85%90%EC%B8%A0-%EC%A0%9C%EA%B3%B5-MVC-RESTful-API-%EA%B5%AC%ED%98%84%EA%B9%8C%EC%A7%80-1</link>
            <guid>https://velog.io/@coding_goat/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EC%9E%85%EB%AC%B8-%EC%A0%95%EC%A0%81-%EC%BB%A8%ED%85%90%EC%B8%A0-%EC%A0%9C%EA%B3%B5-MVC-RESTful-API-%EA%B5%AC%ED%98%84%EA%B9%8C%EC%A7%80-1</guid>
            <pubDate>Sat, 17 Aug 2024 09:51:04 GMT</pubDate>
            <description><![CDATA[<p><em><strong>스프링 부트</strong>를 활용한 웹 애플리케이션 개발은 <strong>정적 컨텐츠</strong> 제공, <strong>MVC 패턴</strong> 적용, <strong>RESTful API</strong> 구축이라는 세 가지 중요한 요소를 포함합니다. 각 포스트에서 한 가지씩 다뤄서 작성할 예정입니다. 
(본 포스트는 인프런 김영한님의 강의 &#39;스프링 입문-코드로 배우는 스프링부트, 웹 MVC, DB 접근 기술&#39;을 보며 추가로 구글링 하여 정리하며 작성한 내용입니다.)</em></p>
<h2 id="정적-컨텐츠의-정의-및-예제">정적 컨텐츠의 정의 및 예제</h2>
<h3 id="1-정적-컨텐츠의-정의">1. 정적 컨텐츠의 정의</h3>
<blockquote>
<p><strong>정적 컨텐츠(static content)</strong>는 서버에서 미리 준비된 상태로 클라이언트에게 제공되는 웹 컨텐츠를 말합니다. 이 컨텐츠는 서버 측에서 별도의 처리나 계산 없이 그대로 클라이언트에게 전달됩니다. 주로 웹사이트의 구조나 디자인을 담당하며, HTML, CSS, 이미지, JavaScript 파일 등이 이에 해당합니다. 정적 컨텐츠는 사용자가 요청할 때마다 동일한 내용을 제공하며, 서버의 리소스를 절약할 수 있어 빠른 응답 속도를 자랑합니다.</p>
</blockquote>
<h3 id="2-정적-컨텐츠의-예시">2. 정적 컨텐츠의 예시</h3>
<blockquote>
<p><strong>HTML 파일</strong>: 웹페이지의 기본 구조를 정의하는 파일로, 페이지의 제목, 문단, 목록 등을 포함합니다.</p>
</blockquote>
<p><strong>CSS 파일</strong>: 웹페이지의 스타일을 정의하는 파일로, 색상, 글꼴, 배경, 레이아웃 등을 지정합니다.</p>
<blockquote>
</blockquote>
<p><strong>이미지 파일</strong>: PNG, JPEG, GIF 등의 이미지 파일로, 웹사이트의 시각적 요소를 제공합니다.</p>
<blockquote>
</blockquote>
<p><strong>JavaScript 파일</strong>: 기본적인 인터랙션을 제공하는 스크립트 파일로, 클라이언트 측에서 실행되는 코드가 포함됩니다.</p>
<blockquote>
</blockquote>
<p><strong>동영상 파일</strong>: 웹사이트에 삽입된 동영상도 정적 콘텐츠의 일종으로, 서버에서 미리 준비된 상태로 제공됩니다.</p>
<h3 id="3-정적-컨텐츠-예제">3. 정적 컨텐츠 예제</h3>
<pre><code>**resources/static/hello-static.html**
&lt;!DOCTYPE HTML&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;static content&lt;/title&gt;
    &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;
&lt;/head&gt;
&lt;body&gt;
정적 컨텐츠 입니다.
&lt;/body&gt;
&lt;/html&gt;</code></pre><blockquote>
<p><img src="https://velog.velcdn.com/images/coding_goat/post/1f3a5f25-6357-4645-acf4-6ca2c4c2b19b/image.png" alt=""></p>
</blockquote>
<blockquote>
<p><strong>1. 요청 처리</strong>
사용자가 localhost:8080/hello-static.html과 같은 URL로 접근하면, 해당 요청이 스프링 부트 애플리케이션으로 들어옵니다.</p>
</blockquote>
<p><strong>2. 스프링 컨테이너에서 컨트롤러 확인</strong>
스프링은 먼저 요청된 URL에 매핑되는 컨트롤러가 있는지 확인합니다. 이 경우, hello-static 관련 컨트롤러가 있는지 찾습니다.</p>
<blockquote>
</blockquote>
<p><strong>3. 컨트롤러가 없는 경우</strong>
만약 해당 요청을 처리할 컨트롤러가 존재하지 않는다면, 스프링은 정적 콘텐츠 폴더(src/main/resources/static)를 검색합니다.</p>
<blockquote>
</blockquote>
<p><strong>4. 정적 파일 제공</strong>
static 폴더에서 hello-static.html 파일이 발견되면, 해당 파일이 브라우저에 전달되어 사용자가 볼 수 있게 됩니다.</p>
<h3 id="4-추가-설명">4. 추가 설명</h3>
<blockquote>
<p>스프링 부트는 기본적으로 src/main/resources/static, src/main/resources/public, src/main/resources/resources, src/main/resources/META-INF/resources 폴더에서 정적 파일을 제공하도록 설정되어 있습니다.</p>
</blockquote>
<p>만약 컨트롤러가 해당 요청을 처리할 수 없고, static 폴더에서도 파일을 찾을 수 없다면, 스프링 부트는 404 Not Found 에러를 반환합니다.</p>
]]></description>
        </item>
    </channel>
</rss>