<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jeong_lululala.log</title>
        <link>https://velog.io/</link>
        <description>성장중입니다 🔥 / 나무위키처럼 끊임없이 글이 수정됩니다!</description>
        <lastBuildDate>Wed, 25 Oct 2023 05:18:54 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jeong_lululala.log</title>
            <url>https://velog.velcdn.com/images/jeong_lululala/profile/70b6cc5b-76a4-48e3-acf7-d51fbbff6a5d/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jeong_lululala.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jeong_lululala" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[이도저도 아닌 팀장이 된 건에 관하여]]></title>
            <link>https://velog.io/@jeong_lululala/%EC%9D%B4%EB%8F%84%EC%A0%80%EB%8F%84-%EC%95%84%EB%8B%8C-%ED%8C%80%EC%9E%A5%EC%9D%B4-%EB%90%9C-%EA%B1%B4%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</link>
            <guid>https://velog.io/@jeong_lululala/%EC%9D%B4%EB%8F%84%EC%A0%80%EB%8F%84-%EC%95%84%EB%8B%8C-%ED%8C%80%EC%9E%A5%EC%9D%B4-%EB%90%9C-%EA%B1%B4%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</guid>
            <pubDate>Wed, 25 Oct 2023 05:18:54 GMT</pubDate>
            <description><![CDATA[<p>팀 프로젝트를 하면서 스트레스를 받았다. 굉장히 많이 받았다. 어쩌면 팀장은 처음이라 당연했다.</p>
<h1 id="팀장을-맡은-이유">팀장을 맡은 이유</h1>
<p>팀장을 맡은 이유는 내 아이디어가 채택 되었기 때문이다. </p>
<p>이 아이디어는 내가 되게 애착하는 아이디어였다. 방학 때 미리 만들어보며 꿈을 크게 만들 만큼 </p>
<p>이 아이디어는 방학 중에 혼자서 작은 사이즈의 프로토타입을 만들면서 애착이 강해진 아이였다. </p>
<p>아이디어에 대한 애착이 강해서 내가 아이디어가 흐지브지 되지 않게 주도적으로 팀을 이끌고 싶었다.</p>
<h1 id="팀장의-자질이-부족했던-나">팀장의 자질이 부족했던 나</h1>
<p>A 가 실수를 하면, A 의 잘못일까? </p>
<p>나는 A 의 잘못이라고 생각했다. 하지만 그건 모두의 잘못이었다.</p>
<p>우리의 하나의 목표를 가지고 나아간다.</p>
<p>누구던 그 목표에 영향이 갈 만한 문제를 발생시킨다면
그것은 팀의 피해이고, 팀의 문제이고, 다같이 해결해야 할 문제이다.</p>
<p>질책이나 타박 방관이 아니라, 다시는 그런 일이 발생하지 않도록 협조해야 하는 것이었다.</p>
<p>너는 너, 나는 나 마인드를 갈아엎는 것.</p>
<p>이것이 스프린트 전에 근육을 키우는 토이 프로젝트 기간을 보내면서 
가장 느낀 점이다.</p>
<h1 id="팀장의-역할을-이해하기까지">팀장의 역할을 이해하기까지</h1>
<p>팀장은 어떤 일을 해야 할까? 이에 대한 정리가 안돼어 있어서,
발을 넣다 뺏다 했던 것 같다.</p>
<h1 id="우리는-쌍방과실">우리는 쌍방과실</h1>
<h1 id="우리는-숙련도-부족">우리는 숙련도 부족</h1>
<h1 id="우리는-이렇게-성장한다">우리는 이렇게 성장한다</h1>
]]></description>
        </item>
        <item>
            <title><![CDATA[산티아고 라이프스타일]]></title>
            <link>https://velog.io/@jeong_lululala/santiago-lifestyle</link>
            <guid>https://velog.io/@jeong_lululala/santiago-lifestyle</guid>
            <pubDate>Wed, 25 Oct 2023 04:25:12 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/kaori-killer/post/eeadf3a8-da56-40a5-8ea4-769e0bd9552d/image.png" alt=""></p>
<h1 id="📚-작가의-목적">📚 작가의 목적</h1>
<blockquote>
<p>답을 찾기 위해 많은 사람이 그랬던 것처럼 나 역시 길 위에서 다시 답을 찾기 위해 걸었다. 답은 늘 내 안에 존재하였고, 길은 그 답을 찾기 위해 노란 화살표를 따라 나를 안내해주었다.</p>
</blockquote>
<p>작가는 <code>산티아고 라이프스타일</code> 안에서 자신의 삶에 대해 생각해보고 더 행복하게 살아갈 수 있게 안내
한다.</p>
<h1 id="📚-독서-1일차">📚 독서 1일차</h1>
<h2 id="여행보다-산티아고-순례길">여행보다 산티아고 순례길</h2>
<blockquote>
<p>다양한 삶을 보고자 전 세계로 여행을 떠나 좋았지만, 그곳들은 나의 삶의 터전이 아닌 여행자가 잠시 머무르는 장소였을 뿐이다.</p>
<p>여행은 돌아오면 그 순간 종료되었다. 일상에 복귀하면 다시 다음 여행을 기다리며 살 수 있게 하는 원동력 정도일 뿐이었다.</p>
<p>여행은 늘 일상 밖의 일이었지만, 산티아고 순례길은 일상이 될 수 있다고 느꼈다.</p>
<p>내 걸음 속도에 맞춰 걷거나 쉬는 것이 어려우니 걸으며 몰입하는 과정 역시 자주 깨졌다.</p>
</blockquote>
<p>다른 문화를 경험하고 싶어서 호주에 있는 이모네로 향했다. 그 집이 호주의 대표적인 문화를 반영한 것은 아니었지만, 서로를 배려하고 소통하고자 하는 의지가 공존하는 환경이었다. 또한 자신감 넘치는 태도에 왠지 부럽지 않을 수 없었다.</p>
<p>그래서 첫 번째 목표은 그들로부터 많은 것을 배우는 것, 두 번째 목표은 호주를 완전히 느끼는 것이었다. 하지만 안타깝게도 언어 장벽으로 인해 2주 동안 정서적 유대감조차 형성하지 못했다. 결국 처음보다 더 어색한 상태로 이별하게 되었다. 두 번째 목표도 쉽지 않았다. 길을 걸으면서 호주를 완전히 느끼고 싶었지만, 이모는 항상 나를 혼자 두지 않았다. 끊임없는 동행은 여행을 불편하게 만들었다. 내 성격상 긴장하고 당황하기 쉬워서 스트레스를 많이 받았고, 5개월이 지난 지금도 꿈에 상황이 나온다. </p>
<p>이 도전적인 여행을 마치고, 나는 나 자신을 발견하고 무언가를 찾고자 하는 여행이 내 것임을 깨달았다. 그러기 위해서는 언어가 장벽이 되면 안되고, 온전한 나만을 위한 시간이 필요하다. 단순한 여행이 아니라는 것에서 이 책을 읽으며 &quot;맞아, 이게 바로 내가 찾던 것이구나&quot; 라고 생각했다.</p>
<h1 id="📚-독서-2일차">📚 독서 2일차</h1>
<h2 id="삼성에-들어가면-꽃길만-있을-줄-알았지">삼성에 들어가면 꽃길만 있을 줄 알았지</h2>
<blockquote>
<p>처음 마음을 졸이며 최종합격을 확인하고 환호성을 친 것에 비교하면 조촐한 마무리였다. 그때는 삼성에만 들어가면 내 모든 인생은 바뀌리라 생각했다. 무엇을 위하여 그토록 노력했던 것일까?</p>
<p>멋진 사회인으로 양복을 입고 세계로 출장을 다니며 비즈니스를 할 거란 기대와 달리 매일 똑같은 옷을 입고 온종일 연구실에서 프로젝트 일정에 맞추기 위한 치열한 전투를 벌이며 밖에 비가 오는지 눈이 내리는지 해가 지는지도 모른 채 앞만 보고 살아갔다.</p>
<p>내가 할 수 있는 일이 없더라도 함께 남아 업무를 배우지 않으면 도태될 것 같았다.</p>
<p>내가 평생을 치열하게 노력해도 될까 말까 한 삼성 임원이 된다해도 그리 행복하지 않다는 것에 놀랐다.</p>
</blockquote>
<p>지금은 대기업 회사라면 무조건 들어가고 싶다. 그곳에서 좀비로 사는 인생을 고려하지 않는다. </p>
<p>그래도 시니어 개발자가 되었을 때는 한 번쯤 작가의 말을 떠올려보자.</p>
<blockquote>
<p>나는 자엽스럽게 스스로 합리화시키며 아내에게 회사에 조금 더 있어야 하는 이유를 이야기하면서 아내를 설득했다.</p>
</blockquote>
<p>정답은 내 안에 있는데, 스스로를 합리화 시키며 상대에게 네 말이 정답이라고 듣고 싶을 때가 있다.</p>
<h1 id="📚-독서-3일차">📚 독서 3일차</h1>
<h2 id="두려움에-자신을-잃어-가는-사람들">두려움에 자신을 잃어 가는 사람들</h2>
<blockquote>
<p>어릴 적 나의 꿈은 대학입시 후 전공학과를 선택하면서부터 현실과 타협하기 시작했다.  </p>
<p>왜 삼성전자에 취업해야 하는지, 무엇을 하고 싶은지에 대한 답은 없었다.</p>
</blockquote>
<p>학창시절부터 우리는 너무 많은 두려움에 노출된다. 나 역시 금전적 두려움과 사회적 시선에 대한 두려움을 가지고 있다. 두려움은 자연스레 동반되는 것이라고 하기엔 나를 갉아 먹는다. </p>
<blockquote>
<p>하루를 불안함으로 시작하였다. 늘 불안을 안고 있다 보니 무엇하나 제대로 할 수가 없었다. 끊임없이 할 일을 만들었고, 머리속에는 늘 해야 할 일로 가득 차 있었다.</p>
<p>미래에 대한 불확실성에 대한 불안은 생존의 불안도 있지만, 사회적 불안도 크게 작용한다. 인간은 사회적 동물이라 생존을 넘어 사회에서 인정받고 싶어 하고 공동체를 이루고 싶어 하고 가까운 친구를 사귀고 싶어 한다.</p>
<p>이해 관계없이 서로를 믿을 수 있는 관계가 형성되면서 나의 불안은 해소될 수 있었다.</p>
<p>능동적 정보획득이 손쉬워짐과 동시에 사회적으로 불안을 야기하는 부작용도 발생했다. 언론 보도를 통해 접한 사회적 이슈로 이미 많은 불안이 생성되고 있는데 SNS 속 나와 비슷한 누군가 혹은 친구들은 모두 잘살고 있고, 잘하고 있는데 나만 잘 못하고 있는 것과 같은 불안이 느껴진다.</p>
<p>나와 비슷한 또래의 누군가 또는 나와 학창 시절을 같이했던 친구가 좋은 차를 타고 좋은 집을 사면 자신과 비교하게 되면서 불안을 느낀다. &#39;내가 지금 잘못하고 있는 것은 아닐까?&#39;, &#39;나는 무엇인가 해야 하지 않을까?&#39; 하는 생각과 함께 판단력을 잃게 되고 남을 따라서 행동하게 된다.</p>
<p>포모(Fear of missing out) 증후군 
소외되는 것에 두려움, 자신만 뒤쳐지고, 놓치고, 제외되는 것 같은 불안감을 느끼는 증상</p>
<p>정확하게 생각하고 판단할 겨를도없이 행동하기도 한다.</p>
<p>내가 원하는 것이 무엇인지 내가 좋아하는 것이 무엇인가를 알지 못하고 내 삶을 사는 대신 누군가의 삶을 살게 된다. 불안이 초래한 삶은 유행을 좇아 열심히 즐기면서 사는데도 불구하고 행복하지 않을 수 있다.</p>
</blockquote>
<p>친한 친구가 하는 말에도 불안감을 느낀다. 그들의 자랑이 나의 도태 위험성을 높인다. </p>
<p>그래서 친구가 많은 사람은 대단하다. 수많은 자랑 속에서 자신만의 삶을 사는 비결이 궁금하다.</p>
<p>나는 불가능하다. 친구는 최소 한 명만 있으면 된다. 스트레스 받은 날 걱정 없이 얘기할 수 있는 친구면 된다. </p>
<p>그리고 스스로 할 수 있는 것은  주변을 신경쓰지 않도록 노력하는 것이다. 내 속도에 맞춰서 걷는 것이다. 다음을 계속 자각한다. 우리는 목표가 다르고 목적이 다른 인생을 살고 있다. 비교 할 수 없다. 부러움에 눈이 멀먼 내가 아닌 타인의 삶을 살고 있을 것이고,  행복은 더욱 멀어져 간다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[미드나잇 라이브러리]]></title>
            <link>https://velog.io/@jeong_lululala/the-midnight-library</link>
            <guid>https://velog.io/@jeong_lululala/the-midnight-library</guid>
            <pubDate>Wed, 25 Oct 2023 04:22:48 GMT</pubDate>
            <description><![CDATA[<h2 id="우연히-내게-오나-봐">우연히 내게 오나 봐</h2>
<p>평택에서 돌아오는 길에 지하철 서점을 둘러보다 우연히 이 책을 발견했다. 처음에는 이 책의 가치에 대해 의구심이 들었다. 그 인기가 주로 방탄소년단의 홍보 활동에서 비롯된 것 같았다. 그런데 책을 훑어보다가 <code>자정의 도서관</code> 이라는 수수께기 같은 단어가 호기심을 자극했다. 그래서 책을 사지 않을 수 없었다.</p>
<h3 id="도입부">도입부</h3>
<blockquote>
<p><code>실비아 플라스</code>
나는 결코 되고 싶은 사람이 다 될 수 없고, 원하는 삶을 모두 살아볼 수도 없다. 원하는 기술을 모두 배울 수도 없다. 그런데도 왜 그러길 바라는가? 난 내 삶에서 일어날 수 있는 정신적 육체적 경험의 모든 음영과 색조와 변주를 살아내고 느끼고 싶다.</p>
</blockquote>
<h2 id="경기는-끝날-때까지-끝난-게-아니야">경기는 끝날 때까지 끝난 게 아니야</h2>
<p>노라는 과거의 후회를 바로 잡은 새로운 삶을 살았지만 거기서도 또 다른 후회를 만들었다. 결국 완벽한 삶은 도달할 수 없는 목표라는 것이 분명해졌다. <code>나를 포함한 모두가 행복한 삶</code> 은 무한히 펼쳐진 평행우주 안에서도 불가능한 것이었다.</p>
<p>결국 노라는 제 인생으로 돌아왔다. 지금 인생이 체스에서 폰 밖에 남지 않은 상황에 심지어 그 폰이 된 심정이지만, 포기하지 않고 앞으로 나아갈 방법을 찾는다면 결국에 경기를 승리로 이끄는 요인이 될 것이다.</p>
<p>그만큼 노라 시드에게는 무한한 가능성이 남아있다.</p>
<blockquote>
<p><code>라비린스 보컬, 노라 시드</code>
살다 보면 더 쉬운 길이 있을 거라고 생각하기 십상이죠. 하지만 아마 쉬운 길을 없을 거예요. 그냥 여러 길이 있을 뿐이죠.</p>
</blockquote>
<blockquote>
<p><code>라비린스 보컬, 노라 시드</code>
매일 매 순간 우리는 새로운 우주로 들어가요. 자신을 타인 그리고 또 다른 자신과 비교하며 삶이 달라지기를 바라는 데 많은 시간을 보내죠. 사실 대부분의 삶에는 좋은 일과 나쁜 일이 공존하는데 말이에요. </p>
<p>한 삶에만 갇혀 있는 동안에는 슬픔이나 비극 혹은 실패나 두려움이 그 삶을 산 결과라고 생각하기 쉽죠. 그런 것들은 단순히 삶의 부산물일 뿐인데 우리는 그게 특정한 방식으로 살았기 때문에 생겨났다고 생각해요. 하지만 슬픔에 면역력이 생기는 삶의 방식은 없다는 걸 이해하면 사는 게 훨씬 쉬워질 거예요. 슬픔은 본질적으로 행복의 일부라는 사실도요. 슬픔 없이 행복을 얻을 수는 없어요. 물론 사람마다 그 정도와 양이 다르긴 하겠죠. 하지만 영원히 순수한 행복에만 머물 수 있는 삶은 없어요. 그런 삶이 있다고 생각하면, 현재의 삶이 더 불행하게 느껴질 뿐이죠.</p>
</blockquote>
<h2 id="다른-사람에게-인정받으려고-전전긍긍하는-건-그만둬야-할지도-몰라">다른 사람에게 인정받으려고 전전긍긍하는 건 그만둬야 할지도 몰라</h2>
<p>나만의 속도로 천천히 주변을 둘러보며 나아가고 싶다. 사소한 것에 감사하는 삶을 살고 싶다. 온전한 나의 모습을 인정하고 받아들이고 싶다.</p>
<blockquote>
<p><code>수영 선수, 노라 시드</code>
내가 아닌 다른 사람이 되는 걸 목표로 한다면 반드시 실패합니다. 나 자신이 되는 걸 목표로 하세요. 나처럼 보이고 행동하고 생각하는 걸 목표로 하세요. 가장 <code>나다운 나</code> 가 되는 걸 목표로 하세요. 나를 나로 만드는 모든 요소를 받아들이세요. 그걸 지지하세요. 사랑하세요. 갈고닦으세요. 사람들이 그걸 조롱하고 비웃을 때 휩쓸리지 마세요. 대부분의 험담은 사실 질투랍니다. 묵묵히 할 일을 하세요. 체력을 키우세요. 계속 수영하세요.</p>
</blockquote>
<blockquote>
<p>노라는 늘 자기 자신을 잘 받아들이지 못했다. 그녀가 기억할 수 있는 어린 시절부터 자신이 어딘가 부족하다고 느꼈다. </p>
<p>지금 이 순간, 노라는 자신을 완전히 받아들인다는 게 어떤 기분일지 상상해봤다. 자신이 저지른 모든 실수와 몸의 모든 흔적, 이루지 못한 모든 꿈 혹은 자신이 느끼는 모든 고통, 꾹꾹 눌러둔 모든 성욕과 욕망까지. </p>
<p>이 모두를 받아들이는 걸 상상했다. 자연을 받아들이듯이. 그러면서 자유롭다는 게 어떤 기분일지 상상했다.</p>
</blockquote>
<h2 id="sns-우울증">SNS 우울증</h2>
<p>외로움이 심해짐에 따라 인간 관계에 대한 욕구가 강해지고 많은 사람들이 그 욕구를 충족시키는 수단으로 SNS를 사용하게 된다. 그러나 온라인 영역은 서로에 대해 제한된 지식을 가진 개인들로 넘쳐난다. 외로움은 사람들을 하나로 모으지만 시기심과 부정으로 인해 적대감을 불러일으킬 수도 있다. 이러한 부정적인 감정은 확대되어 오히려 고립의 주기를 지속시킨다.</p>
<p>그러니 외로움이 찾아온다면 대면으로 대화를 하거나 나 자신을 알아가는 과정에 집중해보자. 나와 나 자신을 연결하면 단단한 사람이 될 것이다.</p>
<h2 id="마무리">마무리</h2>
<blockquote>
<p>모든 게 달라진 이유는 이젠 그녀가 단지 다른 사람의 꿈을 이뤄주기 위해 존재하지 않기 때문이었다. 무언가가 되는 데서 유일한 성취감을 찾아야 한다고 생각하지 않기 때문이었다. 이제는 그저 한 인간으로서 자신의 목표만 생각하며 자신만 책임지면 그만이었다.</p>
<p>노라는 삶이 얼마나 광활한지 경험했고, 그녀가 봤던 그 광활함 속에서 자신이 해낼 수 있는 일뿐 아니라 느낄 수 있는 감정도 한없이 다양하다는 걸 깨달았다.</p>
<p>더는 자신의 고통에서 달아나지 않을 것이다. 자신이 상상하는 완벽한 모습이라는 독으로 스스로를 죽이지 않을 것이다. 자신의 상처를 보고 인정할 것이며 자신에게 박탈된, 한 치의 의심도 없이 긍정적이고 행복한 삶이 있다고 상상하지 않을 것이다. 처음으로 삶의 어두운 면을 받아들일 것이다. 실패가 아니라 전체 중 일부로. 다른 것들을 돋보이게 하고, 성장시키고, 존재하게 하는 무언가로. 흙 속의 거름으로.</p>
</blockquote>
<h2 id="기타">기타</h2>
<blockquote>
<p>중요한 것은 무엇을 보느냐가 아니라 어떻게 보느냐이다</p>
</blockquote>
<blockquote>
<p>부모님이 불행했던 이유는 무언가를 성취하지 못했기 때문이 아니라 애초에 성취하겠다는 기대를 품었기 때문인지도 모른다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Web MVC 를 이용해서 HTTP Server 를 만들어보자]]></title>
            <link>https://velog.io/@jeong_lululala/spring-web-mvc</link>
            <guid>https://velog.io/@jeong_lululala/spring-web-mvc</guid>
            <pubDate>Mon, 02 Oct 2023 13:21:05 GMT</pubDate>
            <description><![CDATA[<h1 id="키워드">키워드</h1>
<ul>
<li><a href="https://docs.spring.io/spring-framework/docs/current/reference/html/overview.html#overview">Spring</a>(링크된 문서에서 핵심을 캐치할 것, 괴롭지만 한 번은 해내야 함)</li>
<li>Spring Boot</li>
<li>Spring initializer</li>
<li>Web Server와 Web Application Server(WAS)<ul>
<li>Tomcat</li>
</ul>
</li>
<li>Model-View-Controller(MVC) 아키텍처 패턴</li>
<li>관심사의 분리(Seperation of Concern)</li>
<li>Spring MVC</li>
<li>Java Annotation</li>
<li>Spring Annotation<ul>
<li>@RestController<ul>
<li>@Controller</li>
<li>@ResponseBody</li>
</ul>
</li>
<li>@GetMapping<ul>
<li>@RequestMapping</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1 id="최종-목표">최종 목표</h1>
<p>HTTP 에 대해서 간단히 알아보고 직접 구현해보자.</p>
<blockquote>
<p>웹의 핵심 요소인 HTTP는 웹 개발자에게 있어서 필수적으로 알아야 하는 지식이다. 그러나 상당수의 개발자들이 HTTP가 어떠한 원리로 동작하는지 제대로 알지 못하고 있다. 이번 과정을 통해 HTTP 통신을 처리하는 웹 서버가 의외로 간단한 원리로 구현되어 있다는 걸 알게 될 것이다. 직접 간단한 웹 서버를 구현해보면서, HTTP를 제대로 이해하고 동시에 Spring Web MVC가 어떤 편의를 제공하는지 절실하게 느껴보자.</p>
</blockquote>
<h1 id="현재-목표">현재 목표</h1>
<p>Spring Web MVC 를 이용해서 HTTP Server 를 만들어보자.</p>
<h1 id="spring-프로젝트-만들기">Spring 프로젝트 만들기</h1>
<ul>
<li><a href="https://start.spring.io/">Spring initalize</a></li>
</ul>
<ol>
<li>Spring initalize 에서 선택하면 된다. 다음 이미지와 같이 선택한다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/f1209022-9caa-408e-aee3-22d309e4d4a3/image.png" alt=""></p>
<ol start="2">
<li>그리고 Dependecney 를 추가하자. <code>Devtools</code> 는 코드를 고치면 컴파일이 되고 자동으로 재시작을 해준다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/bc5564cc-4b5a-4d13-a91c-b982665c5310/image.png" alt=""></p>
<ol start="3">
<li>선택이 끝났으면 generate 버튼을 누른다. 그러면 프로젝트 압축 파일이 다운된다. </li>
</ol>
<h1 id="프로젝트-폴더-설정하기">프로젝트 폴더 설정하기</h1>
<p>원하는 폴더로 다운받은 폴더를 이동시키고 압축을 해제한다.</p>
<pre><code class="language-bash">mv ~/Downloads/server.zip . # 경로 이동
unzip server.zip # 압축 해제
mv server http-server-3 # 폴더 이름 변경</code></pre>
<p>그리고 2가지 설정을 해준다. </p>
<ol>
<li><code>OnSave</code> 상단의 2개를 체크한다.</li>
<li>Run Server 를 Gradle 로 잡아준다.<ul>
<li>Spring Boot 로 하기 때문에 Boot Run 이라는 게 따로 있다.</li>
</ul>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/335cbf17-50db-4011-9ca5-46af23a9ed1b/image.png" alt=""></p>
<blockquote>
<p>참고로 Spring Boot 는 기본 Port 가 8080이다. 바꾸고 싶으면 Application.properties 폴더에 가서 <code>server.port = 8080</code> 을 적으면 된다.</p>
</blockquote>
<h1 id="mvc-란">MVC 란?</h1>
<p>Model-View-Controller  이다. MVC 패턴이라고 부른데, 여기서 <code>패턴</code> 은 아키텍처 패턴을 의미한다. </p>
<ul>
<li>패턴<ul>
<li>디자인 패턴</li>
<li>아키텍처 패턴</li>
</ul>
</li>
</ul>
<p>MVC는 UI쪽에서 쓴다. 여기서 UI는 웹 말고 화면에 보이는 부분만을 얘기를 한다.</p>
<h1 id="mvc-패턴의-각-의미는">MVC 패턴의 각 의미는?</h1>
<ul>
<li>View - 표현 (눈에 보이는 보는 것)</li>
<li>Controller - 입력 (입력에 대한 처리)</li>
<li>Model - 그 외의 모든 것</li>
</ul>
<p>왜 얘네를 이렇게 나눠줄까? <strong>Model 에 대해서 2가지 의미</strong>가 있기 때문이다.</p>
<h2 id="1-관심사의-분리">1. 관심사의 분리</h2>
<p>MVC 는 <strong>관심사의 분리를 추구</strong>한다. (흔히 말하는 UI와 비즈니스 로직의 분리.)</p>
<p>Model 에 핵심 비지니스가 들어가도록 한다. </p>
<p>예를 들어, 스위치가 있다. flog 가 모델에 있다면, Controller 에서는 <code>flog.toggle()</code> 을 해줄 수 있다. 이것의 내부 처리는 Model 이 알아서 한다. (비지니스 로직)</p>
<h2 id="2-view-에서-참조하는-표현데이터">2. View 에서 참조하는 표현/데이터</h2>
<p>웹에서는 이렇게 일반적으로 이 느낌으로 쓰인다. (원래 MVC와 다르게, 일반적으로 쓰이는 것.)</p>
<blockquote>
<p>내용이 어려워서.. 나중에 이해해보자.</p>
<ol>
<li>Rails는 Active Record 아키텍처 패턴을 통해 Model과 DB를 긴밀하게 연결시켰고, Rails의 성공은 다른 곳에도 많은 영향을 끼쳤다.<ul>
<li><a href="https://martinfowler.com/eaaCatalog/activeRecord.html">Active Record</a></li>
<li><a href="https://steveklabnik.com/writing/active-record-considered-harmful">ActiveRecord considered harmful</a></li>
</ul>
</li>
<li>Spring Web MVC는 Map과 유사한 Model 인터페이스를 제공.<ol>
<li><a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/ui/package-summary.html">https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/ui/package-summary.html</a></li>
<li>애플리케이션 전체가 아니라, 웹과 관련된 부분(UI Layer)에서만 MVC 개념을 활용할 수 있도록 도움. 관심사의 분리를 더 심화할 기회 제공.</li>
</ol>
</li>
</ol>
</blockquote>
<h1 id="지금은-controller-만-쓴다">지금은? Controller 만 쓴다</h1>
<p>최근에는 더 복잡한 걸 잘 다룰 수 있는 더 커다란 아키텍처가 필요하다는 걸 사람들이 알게 되었다.</p>
<p>추세가 백엔드는 API 만 제공하고 프론트에 대한 건 따로 처리해주는 식이기 때문이다.</p>
<p>그래서</p>
<blockquote>
<p>Controller 만으로 충분한데? </p>
</blockquote>
<p>그리고 더 나아가 </p>
<blockquote>
<p>Controller 라는 표현도 필요없어, Handler 로 충분할 거 같아</p>
</blockquote>
<p>Handler 로 충분한 이유는 뭘까?</p>
<p><code>Controller 를 쓰게 되면 나머지는 뭔데?</code> 가 된다. <code>Model 은 뭔데? Model 은 Map 일 뿐이다..?</code> , <code>View 는 템플릿 쓸껀가..?</code> 이렇게 불분명하게 된다. 그래서 <code>그냥 Handler 라고 하자!</code> 가 됐다.</p>
<h1 id="controller-만들기">Controller 만들기</h1>
<p>엄청나게 간단하게 만들 수 있다.</p>
<p>HTTP 의 기본을 익히기 위해서 여러 방법을 사용해봤는데, 앞으로는 Spring Web MVC 만 사용할 것이다.</p>
<p>앞에서 배웠으니 이제 Spring Web MVC 가 마법이 아니라, 뒤에서 무슨 일이 있어났는지 알 수 있다.</p>
<p>내부를 완전 분석 할 필요는 없고 어떤 느낌인지는 알아야 한다.</p>
<blockquote>
<p>뒤에서 무슨 일이 일어나는지 꼭 알야아 해요?</p>
</blockquote>
<p>라고 누군가 말한다면 다음과 같이 대답해주자.</p>
<blockquote>
<p>나중에 더 복잡한 상황이 오거나 에러 대응을 해줘야 할 때,
개발자 도구를 열어서 네트워크를 추척할 때,
모니터링을 할 때,</p>
<p>도움이 되기 위해서 HTTP 기본을 공부한 것이다!</p>
</blockquote>
<h2 id="전체-코드">전체 코드</h2>
<pre><code class="language-java">package com.ahastudio.http.server.controllers;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class WelcomeController {
    @GetMapping(&quot;/&quot;)
    public String home() {
        return &quot;Hello, world\n&quot;;
    }

    @GetMapping(&quot;/hi&quot;)
    public String hi() {
        return &quot;Hi, world\n&quot;;
    }
}</code></pre>
<h1 id="어노테이션을-만나면-확인하는-습관을-들이자">어노테이션을 만나면 확인하는 습관을 들이자</h1>
<p><code>@</code> 이런식으로 어노테이션을 쓰게 된다. 앞으로 이 어노테이션이 보이면 전부 들어가서 뭔지 확인하는 습관을 들이자.</p>
<h2 id="responsebody-란">@ResponseBody 란?</h2>
<p><code>@ResponseBody</code> 를 예로 한번 보자.</p>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/be63a694-b709-46e5-a90f-3eb5e7359306/image.png" alt=""></p>
<p><code>@Controller</code> 와 <code>@ResponseBody</code> 가 붙었다. </p>
<p><code>@ResponseBody</code> 를 붙인게 핵심인데, 이게 없었다면 <code>resources/templates</code> 아래에 있는 걸 잡아서 쓰게 된다.</p>
<p>이걸 붙였기 때문에 다음과 같이 ResponseBody 를 잡아줄 수 있었다.</p>
<blockquote>
<p>나 이거 아니고 body 직접 적으로 해줄 거야</p>
</blockquote>
<p>그래서 우리가 &quot;Hello, world&quot; 라는 String 해줬을 때, 이 값이 그대로 출력되게 된다.</p>
<h2 id="getmapping-란">@GetMapping 란?</h2>
<p><code>@requestMapping</code> 해준다.
Method 랑 Path 를 알아야 한다.
그런데 Method 는 이미 Get 으로 잡혔다.
그래서 우리가 Path 만 잡아줘도 된다.</p>
<blockquote>
<p>이런식으로 어노테이션에 대해 알아가면 된다.</p>
</blockquote>
<h1 id="다음에는">다음에는</h1>
<p>View Template 이용해서 HTML 을 짠! 하게 할 수 있는데, 앞서 말했듯이
요즘은 백엔드는 REST API로 쓰고
프론트는 리액트나 다른 걸로 구성하는 추세이다.
그래서 REST API 에 대해 자세히 다뤄보자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Gradle로 Java 프로젝트 시작하기 (참고용)]]></title>
            <link>https://velog.io/@jeong_lululala/gradle-java-prodjct</link>
            <guid>https://velog.io/@jeong_lululala/gradle-java-prodjct</guid>
            <pubDate>Mon, 02 Oct 2023 09:25:10 GMT</pubDate>
            <description><![CDATA[<h1 id="gradle-로-프로젝트-만들기">Gradle 로 프로젝트 만들기</h1>
<pre><code class="language-bash">mkdir demo
cd demo
gradle init</code></pre>
<p>다음은 예시이다. 상황에 맞게 적절하게 선택하면 된다.</p>
<pre><code class="language-bash">sojeongyoo@Sojeongui-MacBookAir http-client % gradle init

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 2

Select implementation language:
  1: C++
  2: Groovy
  3: Java
  4: Kotlin
  5: Scala
  6: Swift
Enter selection (default: Java) [1..6] 3

Generate multiple subprojects for application? (default: no) [yes, no] no

Select build script DSL:
  1: Kotlin
  2: Groovy
Enter selection (default: Kotlin) [1..2] 2

Select test framework:
  1: JUnit 4
  2: TestNG
  3: Spock
  4: JUnit Jupiter
Enter selection (default: JUnit Jupiter) [1..4] 4

Project name (default: http-client): client
Source package (default: client): com.ahastudio.http.client
Enter target version of Java (min. 7) (default: 18):
Generate build using new APIs and behavior (some features may change in the next


&gt; Task :init
To learn more about Gradle by exploring our Samples at https://docs.gradle.org/8.3/samples/sample_building_java_applications.html</code></pre>
<h1 id="intellij-로-열기">intellij 로 열기</h1>
<pre><code class="language-bash">idea .</code></pre>
<h1 id="intellij-설정하기">intellij 설정하기</h1>
<h2 id="actions-on-save">Actions on Save</h2>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/1d5183bf-a855-483f-ba42-4a0d2ed7b8ff/image.png" alt=""></p>
<h2 id="기본-파일-수정">기본 파일 수정</h2>
<p><code>App.Test.java</code> 파일을 다음과 같이 수정한다.</p>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/3b096c81-f496-44e5-a725-985462748beb/image.png" alt=""></p>
<p><code>App.java</code> 파일을 다음과 같이 수정한다.</p>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/edbfa1c9-184d-4635-9af3-da8f0c4e5a33/image.png" alt=""></p>
<h2 id="gradle-로-시작하도록-수정">Gradle 로 시작하도록 수정</h2>
<p>Gradle 로 시작하도록 수정한다.</p>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/4e7dc6b3-1260-4e6f-8444-a463d1b3ece3/image.png" alt=""></p>
<h1 id="터미널에서-요청-보내는-방법">터미널에서 요청 보내는 방법</h1>
<pre><code class="language-bash">curl localhost:8080

or

http localhost:8080</code></pre>
<p>Body 에 데이터를 넣고 싶다면?</p>
<pre><code class="language-bash">http localhost:8080 name=tester</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[REST API 에 대해 알아보자]]></title>
            <link>https://velog.io/@jeong_lululala/rest-api</link>
            <guid>https://velog.io/@jeong_lululala/rest-api</guid>
            <pubDate>Wed, 27 Sep 2023 05:49:16 GMT</pubDate>
            <description><![CDATA[<h1 id="키워드">키워드</h1>
<ul>
<li>API(Application Programming Interface)</li>
<li>정보은닉(Information Hiding)과 캡슐화(Encapsulation)<ul>
<li>그리고 이 둘의 차이(많이들 혼용하니까 잘 알아두세요)</li>
</ul>
</li>
<li>Architecture와 Architecture Style의 차이</li>
<li>REST(7가지 제약 조건 위주로 정리)<ul>
<li>교재에 나온 <code>필딩 제약 조건</code>을 좀 더 풀어서 정리해보세요.</li>
<li>아래 2가지 자료를 보시면 도움이 되실 겁니다.</li>
<li><a href="https://www.youtube.com/watch?v=RP_f5dMoHFc">https://www.youtube.com/watch?v=RP_f5dMoHFc</a></li>
<li><a href="https://blog.npcode.com/2017/03/02/%eb%b0%94%ec%81%9c-%ea%b0%9c%eb%b0%9c%ec%9e%90%eb%93%a4%ec%9d%84-%ec%9c%84%ed%95%9c-rest-%eb%85%bc%eb%ac%b8-%ec%9a%94%ec%95%bd/">https://blog.npcode.com/2017/03/02/바쁜-개발자들을-위한-rest-논문-요약/</a></li>
<li><a href="https://blog.npcode.com/2017/04/03/rest%ec%9d%98-representation%ec%9d%b4%eb%9e%80-%eb%ac%b4%ec%97%87%ec%9d%b8%ea%b0%80/">https://blog.npcode.com/2017/04/03/rest의-representation이란-무엇인가/</a></li>
</ul>
</li>
</ul>
<h1 id="최종-목표">최종 목표</h1>
<h1 id="학습-목표">학습 목표</h1>
<p>REST API 에 대해 알아보자.</p>
<h1 id="fe-와-be-은">F/E 와 B/E 은?</h1>
<p>커다란 시스템을 만들 때, 시스템을 하나로 만들 수도 있지만, 보통은 시스템을 여러 Tier(또는 Layer) 로 구분한다.</p>
<p>프로그램을 하나로만 만드는게 아니라, 여러 개의 구성된 묶음으로 만든다. 여러 조각.</p>
<p>UI 를 다루는 Layer, 비지니스 로직을 다루는 Layer 이런 식으로.</p>
<p>일반적으로</p>
<p>Tire 는 물리적인 구분
Layer 는 논리적인 구분</p>
<p>버튼 보여주고, 글 보여주고</p>
<p>계산을 해요, 값을 만들어요</p>
<hr>
<p>프로그램은 원격으로 쓰게 된다. </p>
<p>다른 곳을 가서 써도 동일하게 보임. 내 컴퓨터 안에서만 돌아가는 게 아님.
구글 독스</p>
<p>이렇게 프로그램을 여러 조각으로 나눴을 때, 사용자에게 가까운 부분이 F/E 라고 한다. 먼 부분이 B/E 라고 한다.</p>
<hr>
<p>F/E 와 B/E 는 어떤 식으로든 연결이 되야 한다.</p>
<p>얘네를 물론 한 프로그램 안에서 할 수도 있다.</p>
<p>하지만 이전에 말했듯이 얘네도 Tier 로 나눠지게 된다. </p>
<hr>
<p>얘네를 물리적으로 분리를 해놓고 
사이의 통신은 HTTP 로 해준다. (이건 통신 규약일 뿐이다. HTTP 자체는 프로토콜일 뿐이고) 
다양한 웹 기술을 이용한다.  그리고 동시에 REST API 를 사용한다.</p>
<p>REST 라는 아키텍쳐 스타일을 따르는 API 이다.</p>
<p>REST API 만 가능한 건 아니다. GraphQL, SOAP 등 다른 것도 사용할 수 있다.</p>
<hr>
<p>물리적으로 완전히 나눠보면</p>
<p>서버는 백엔드다. 클라이언트는 프론트엔드다.</p>
<p>프론트엔드를 개발한다?
웹 브라우저(클라이언트 프로그램) 위에서 돌아가는 자바스크립트 프로그램을 만드는 것.</p>
<p>백엔드를 개발한다?
옛날에는 HTTP 로 통신하는 걸 다 짜줬지만 지금은 Tomcat 웹 애플리케이션 서버가 떠있고 그 위에서 돌아가는 자바 서블릿, 스프링 부트 를 통해서 프로그램을 개발하는 것</p>
<p>이게 아니라면 Tier 로써의 백엔드라고 하면
소켓 프로그래밍부터 다 만드셨나요?
브라우저부터 다 만드셨나요?
이렇게 된다. </p>
<p>우리가 모든 부분을 만드는 게 아니라, 서비스를 제공하기 위한 부분들을 만드는 것이다.</p>
<h1 id="api-란">API 란?</h1>
<p>Application Programming interface</p>
<p>= 애플리케이션을 만들기 위한 인터페이스이다.</p>
<p>=&gt; 객체 지향에서의 인터페이스와는 조금 다르긴 하지만,
시그니처, 명세, 스펙</p>
<p>구현과 상관없다! </p>
<p>이렇게 쓰는 거야. 이렇게 될 거야. (약속, 계약) =&gt; 명세, 스펙 =&gt; 을 모아놓을 것이 인터페이스이다.</p>
<p>interface 로 얻을 수 있는 효과</p>
<ul>
<li>Communication<ul>
<li>구현과 상관없이, 어떻게 소통할지에 대해서 명세</li>
</ul>
</li>
<li>Specification<ul>
<li>어떻게 할지 드러내지 않는다. interface 만 있기 때문. 연결점만 있는 것.</li>
</ul>
</li>
<li>Imformation Hidong (Principle)=원칙<ul>
<li>즉, 무슨 일이 일어나는지는 어딘가의 뒤로 감춰진다. (=정보은닉) 내부가 아니라, 벽치고 뒤에 있는 느낌</li>
<li>어떻게 기술적으로 정보은닉을 할까 -&gt; 캡슐화 != 정보은닉</li>
<li>한 곳에 묶어서 내부를 만든다. 내부와 외부를 연결은 인터페이스로만 가능하다. -&gt; 밖으로 안에가 드러나지않는다.</li>
<li>기술적으로 정보 은닉을 하는 방법 중에 하나가 캡슐화이다.</li>
</ul>
</li>
<li>Implementaion<ul>
<li>스위치라고 했을 때 우리는 내부 작동을 전혀 모르지만 On/oFF 는 함. 구현과 구분하기 위해서 인터페이스를 많이 쓴다.</li>
</ul>
</li>
</ul>
<p>=&gt; 결론 </p>
<p>프로그래밍 하기 위해서 나혼자 할 수 없음. 누군가가 제공함. </p>
<p>스위치가 있어서 눌렀더니 뭔가가 일어나는 스펙에 대한 것.</p>
<hr>
<p> 인터페이스를 쓰는 방법은?</p>
<p> 그냥 누르는 스위치</p>
<p> 슬라이드 스위치</p>
<p> 다이얼 스위치</p>
<p> 쓰는 방법이 다를 수 이싿.</p>
<p> 쓰는 방법이 원칙을 따르면 좋겠다고 생각. =&gt; 그 중 하나로 REST 원칙를 따르자.</p>
<h1 id="rest-란">REST 란?</h1>
<p>Representaion State Transfer (표현적인상태전송)</p>
<p>REST</p>
<p>로이 필딩이 논문을 그중에 REST 에 대한 게 있다. 아키텍쳐 스타일. 네트워크를 베이스(기반으로)로 하는 소프트웨어.</p>
<p>REST는 아키텍쳐를 위한 아키텍쳐 스타일로 볼 수 있다.</p>
<hr>
<p>REST 제약 조건</p>
<ul>
<li>String With Null Style</li>
<li>C </li>
<li>S</li>
<li>C
여기까지는 웹에 있는 거</li>
<li>Uniform Interface -&gt; 핵심</li>
</ul>
<p>필딩 제약 조건 (4가지)</p>
<ul>
<li><p>URI 등으로 리소스를 식별할 수 있다.</p>
</li>
<li><p>표현으로 리소스를 조작한다.</p>
</li>
<li><p>자기서술적 메시지여야한다.</p>
<ul>
<li>여러 layer 가 나눠져이썽ㅆ다. 그 안에서 얘를 처리하거나 변환할 수 있어야 되는데, 내가 누군지 알아야 한다. name &lt;- 컴퓨터: 이게 뭔데? 사람: 이름이네</li>
<li>그래서 MINE 타입으로 설명을 넣어야 한다.  </li>
</ul>
</li>
<li><p>줄여서 HATEOAS 라고 부른다. </p>
</li>
<li><blockquote>
<p>rest api 얘기할 때 까다로운 부분 (= 많이 안지킴)</p>
</blockquote>
</li>
</ul>
<hr>
<p>REST 를 얘기할 때 &#39;필딩 제약 조건&#39; 을 따르느냐 안 따르느냐에 집중한다.</p>
<p>REST API </p>
<p>API 를 구성할 때 REST 하게 만들거야. 
근데 원래 REST 는 API 를 위한 아키텍쳐는 아니다.
웹에서 일반적인 경우에 특화된 것 뿐.</p>
<p>하지만 제약 조건을 완전히 지키지 않더라도 조금만 지키더라도 aPI 를 만들 때 굉장히 유용하게 활용할 수 있다. (일종의 표준을 따르는 거니까)</p>
<hr>
<p>리소스는 무엇? 추상O 실제X</p>
<p>파일 자체가 리소스(X) 서버에 저장된 무언가가 아님!</p>
<p>리소스는 모든 시간에 통용되는 엔티티의 집합이다.</p>
<p>엘리스가 키가 커지더 작던 엘리스이다. (내부 속성이 바뀌어도 엘리스이다)</p>
<hr>
<p>리소스와 표현을 분리함</p>
<p>Representaion -&gt; Data + MetaData === &gt; 사실상 HTTP Message</p>
<hr>
<p>리소스, 표현, 실제 데이터 =&gt; 얘네를 구분하는게 레스트에서 강력한 부분</p>
<hr>
<p>2번 잘 정리해보장</p>
<p>스펙 정한 거를 API 문서라고 한다. =메뉴열</p>
<p>메뉴열을 보고 </p>
<hr>
<p>단계가 있음. 리소스만 도입해도 : 레스트에 한발 다다갔군요!
2단계 리소스에(URL) 대한 처리를 HTTP MEthod 를 썼어</p>
<p>로이 필딩이 대노해도
엽계에선 레벨2까지만 만족해도 레스트 API라고 부른다</p>
<hr>
<p>논문은 REST 를 다룰 것이다.</p>
<p>REST API 가 뭔가요? 설명하기 복잡합니다... 앞으로 공부하면서 일반적으로  REst api 는 이런 개념으로 통용적으로 쓰는 구나 를 파악하자.</p>
<h1 id="아하-포인트">아하! 포인트</h1>
<p>빡세게 이해할 필요는 없다.</p>
<p>REST API 에 대한 개념없이 REST API 를 작성하고 있었다.
통용되는 규칙을 알게 모르게 지키고 있었다는 사실이 충격적이다.
REST API, Resource 의 개념이 확실히 와닿진 않지만 남은 강의를 일단 들어봐야겠다.</p>
<h1 id="다음에는">다음에는?</h1>
<p>REST API 를 이해하기 위한 구성요소들을 하나씩 알아보자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java HTTP Server 를 이용해서 훨씬 더 쉽게 HTTP Server 를 만들어보자]]></title>
            <link>https://velog.io/@jeong_lululala/java-http-server</link>
            <guid>https://velog.io/@jeong_lululala/java-http-server</guid>
            <pubDate>Tue, 26 Sep 2023 01:01:32 GMT</pubDate>
            <description><![CDATA[<h1 id="키워드">키워드</h1>
<ul>
<li>Java HTTP Server</li>
<li>Java NIO</li>
<li>Java Lambda expression(람다식)<ul>
<li>Java Functional interface(함수형 인터페이스)</li>
</ul>
</li>
</ul>
<h1 id="최종-목표">최종 목표</h1>
<p>HTTP 에 대해서 간단히 알아보고 직접 구현해보자.</p>
<blockquote>
<p>웹의 핵심 요소인 HTTP는 웹 개발자에게 있어서 필수적으로 알아야 하는 지식이다. 그러나 상당수의 개발자들이 HTTP가 어떠한 원리로 동작하는지 제대로 알지 못하고 있다. 이번 과정을 통해 HTTP 통신을 처리하는 웹 서버가 의외로 간단한 원리로 구현되어 있다는 걸 알게 될 것이다. 직접 간단한 웹 서버를 구현해보면서, HTTP를 제대로 이해하고 동시에 Spring Web MVC가 어떤 편의를 제공하는지 절실하게 느껴보자.</p>
</blockquote>
<h1 id="현재-목표">현재 목표</h1>
<p>Java HTTP Server 를 이용해서 훨씬 더 쉽게 HTTP Server 를 만들어보자.</p>
<h1 id="이전에는">이전에는?</h1>
<p>지금까지 우리는 저수준으로 했다. 자바에는 고수준의 API 가 준비되어 있다. 이걸 사용해보자.</p>
<p>Java HTTP Server 는 내부적으로 NIO 를 쓴다. (Non-Blocking IO)
우리가 그냥 하는 것보다 훨씬 더 효율적으로 되는 걸 기대할 수 있다.</p>
<h1 id="1️⃣-서버-객체-준비">1️⃣ 서버 객체 준비</h1>
<h2 id="전체-코드">전체 코드</h2>
<pre><code class="language-java">package com.ahastudio.http.server;

import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.net.InetSocketAddress;

public class App {
    public static void main(String[] args) throws IOException {
        App app = new App();
        app.run();
    }

    private void run() throws IOException {
        // 서버 객체 준비
        InetSocketAddress address = new InetSocketAddress(8080);
        HttpServer httpServer = HttpServer.create(address, 0);

        httpServer.start();
    }
}</code></pre>
<h2 id="결과">결과</h2>
<p><code>curl localhost:8080</code> 을 해보자. 404 에러는 나지만 성공적으로 페이지가 나온다.</p>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/45ce882b-55d1-439f-a673-d3142577dfa5/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/4c050550-23dd-4261-ad30-8a52d2d40740/image.png" alt=""></p>
<h1 id="2️⃣-url-정확히는-path-에-핸들러-지정">2️⃣ URL (정확히는 Path) 에 핸들러 지정</h1>
<p>이번에는 주소에 따라 처리하도록 해보자. </p>
<p>HTTPHandler 인터페이스가 가지고 있는 메소드가 하나라서 다음과 같이 람다를 사용할 수 있다.</p>
<pre><code class="language-java">httpServer.createContext(&quot;/&quot;, (exchange) -&gt; {
    // TODO  
});</code></pre>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/cb890ce5-cca3-467a-a8c6-3b48cad7b7e6/image.png" alt=""></p>
<h2 id="전체-코드-1">전체 코드</h2>
<pre><code class="language-java">package com.ahastudio.http.server;

import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.net.InetSocketAddress;

public class App {
    public static void main(String[] args) throws IOException {
        App app = new App();
        app.run();
    }

    private void run() throws IOException {
        InetSocketAddress address = new InetSocketAddress(8080);
        HttpServer httpServer = HttpServer.create(address, 0);

        // URL (정확히는 Path) 에 핸들러 지정
        httpServer.createContext(&quot;/&quot;, (exchange) -&gt; {

        });

        httpServer.start();
    }
}</code></pre>
<h1 id="3️⃣-listen">3️⃣ Listen</h1>
<p>앞 코드에서도 계속 써주긴 했다.</p>
<pre><code class="language-java">httpServer.start();</code></pre>
<h1 id="👉👉-request">👉👉 Request</h1>
<p>위에서 Listen 까지 코드를 작성하고 실행해보면 물려있는 걸 볼 수 있다. 아무것도 처리를 안해줘서 묶여있는 것이다.</p>
<p>우리는 무엇을 알아야 할까? HTTP 요청 메시지을 알고 싶다.</p>
<ul>
<li>Start line<ul>
<li>HTTP MEthod</li>
<li>path</li>
</ul>
</li>
<li>Headers</li>
<li>빈 줄</li>
<li>Body</li>
</ul>
<p>얘네 전부 exchange 에서 얻을 수 있다.</p>
<h2 id="전체-코드-2">전체 코드</h2>
<p><code>http localhost:8080 name=tester</code> 을 해보자. 성공적으로 데이터가 나온다.</p>
<pre><code class="language-java">package com.ahastudio.http.server;

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.List;

public class App {
    public static void main(String[] args) throws IOException {
        App app = new App();
        app.run();
    }

    private void run() throws IOException {
        InetSocketAddress address = new InetSocketAddress(8080);
        HttpServer httpServer = HttpServer.create(address, 0);

        httpServer.createContext(&quot;/&quot;, (exchange) -&gt; {
            // 1. Request
            String method = exchange.getRequestMethod();
            System.out.println(&quot;Method: &quot; + method); // `HTTP MEthod` 출력 결과: GET

            URI uri = exchange.getRequestURI();
            String path = uri.getPath();
            System.out.println(&quot;Path: &quot; + path); // `path` 출력 결과: /

            Headers headers = exchange.getRequestHeaders();
            for (String key : headers.keySet()) {
                List&lt;String&gt; values = headers.get(key);
                System.out.println(key + &quot;: &quot; + values);
                // `Headers` 출력 결과 ⬇️
                // Accept: [*/*]
                // Host: [localhost:8080]
                // User-agent: [curl/8.1.2]
            }

            InputStream inputStream = exchange.getRequestBody();
            byte[] bytes = inputStream.readAllBytes();
            String body = new String(bytes);

            System.out.println(body);
            // 요청하면서 넣은 데이터가 없기 때문에 아무것도 안 나온다

            // httpie 를 이용하면 쉽게 Body에 JSON 데이터를 넣을 수 있다.
            // http localhost:8080 name=tester

              // `Body` 출력 결과 ⬇️
             // {&quot;name&quot;: &quot;tester&quot;}        
        });

        httpServer.start();
    }
}

</code></pre>
<h2 id="결과-1">결과</h2>
<p><code>curl localhost:8080</code> 을 해보자. 성공적으로 결과가 나온다.</p>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/ba262b2b-1828-41c4-85f6-92d418891d79/image.png" alt="">
<img src="https://velog.velcdn.com/images/jeong_lululala/post/af5018dc-5720-48f7-b16b-47bd50740d52/image.png" alt=""></p>
<h1 id="👈👈-response">👈👈 Response</h1>
<p>Response 를 안 주니까 여전히 물려있다.</p>
<p>Response 를 할 때 <code>HTTP Status Code</code> 와 <code>Content-length</code> 는 항상 줘야한다. 이때 Content-length 바이트로 잡아야 한다. 그래서 중간에 바이트로 한 번 변환 후에 길이로 넘겨준다.</p>
<pre><code class="language-java">`byte[] bytes = content.getBytes();`</code></pre>
<p>그리고 재밌는건 브라우저에서 <code>/</code> 아래에 어떤 경로를 추가해도 같은 곳으로 요청을 한다.
그래서 만약 따로 해주고 싶다면 다음 코드로 주소 계속 추가해주면 다른 것도 처리할 수 있다.</p>
<pre><code class="language-java"> httpServer.createContext(&quot;path_name&quot;, (exchange) -&gt; { // TODO }</code></pre>
<h2 id="전체-코드-3">전체 코드</h2>
<p>중복되는 코드가 많아서 <code>Refactor -&gt; Extract Method</code> 로 함수 추출을 해서 작성했다.</p>
<pre><code class="language-java">package com.ahastudio.http.server;

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.List;

public class App {
    public static void main(String[] args) throws IOException {
        App app = new App();
        app.run();
    }

    private void run() throws IOException {
        InetSocketAddress address = new InetSocketAddress(8080);
        HttpServer httpServer = HttpServer.create(address, 0);

        // Path : &quot;/&quot;
        httpServer.createContext(&quot;/&quot;, (exchange) -&gt; {
            // 1. Request
            displayRequest(exchange);

            // 2. Response
            String content = &quot;Hello, world\n&quot;;
            sendContent(exchange, content);
        });

        // Path : &quot;/hi&quot;
        httpServer.createContext(&quot;/hi&quot;, (exchange) -&gt; {
            // 1. Request
            displayRequest(exchange);

            // 2. Response
            String content = &quot;Hi, world\n&quot;;
            sendContent(exchange, content);
        });

        httpServer.start();
    }

    // Request 에서 쓰는 함수 (이름 짓기가 애매해서 displayRequest로 했다. request 를 화면에서 보는 거니까?) 
    private void displayRequest(HttpExchange exchange) throws IOException {
        String method = exchange.getRequestMethod();
        System.out.println(&quot;Method: &quot; + method);

        URI uri = exchange.getRequestURI();
        String path = uri.getPath();
        System.out.println(&quot;Path: &quot; + path);

        Headers headers = exchange.getRequestHeaders();
        for (String key : headers.keySet()) {
            List&lt;String&gt; values = headers.get(key);
            System.out.println(key + &quot;: &quot; + values);
        }

        InputStream inputStream = exchange.getRequestBody();
        String body = new String(inputStream.readAllBytes());

        System.out.println(body);
    }

    // Response 에서 쓰는 함수
    private void sendContent(HttpExchange exchange, String content) throws IOException {
        byte[] bytes = content.getBytes();

        exchange.sendResponseHeaders(200, bytes.length);

        OutputStream outputStream = exchange.getResponseBody();
        outputStream.write(bytes);
        outputStream.flush();
    }
}</code></pre>
<h2 id="결과-2">결과</h2>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/5605c0a2-0e81-4cf9-b2dd-b0cba415ebf9/image.png" alt=""></p>
<h1 id="아하-포인트">아하! 포인트</h1>
<p>자바에 기본으로 있는 HTTP Server 를 써봤다.
Socket 으로 했을 때보다 훨씬 쉽게 경로 여러 개에 대해 처리할 수 있었다.
exchange 를 이용해서 getRequestMethod 함수를 이용하는 부분도 이전과 달리
파싱으로 하지 않고 바로 얻어서 편리했다. 또한 Header 에 대한 부분도 key, value 로 얻을 수 있었다.</p>
<h1 id="다음에는">다음에는</h1>
<p>다음에는 Spring 을 이용해서 이것보다 훨씬 쉽게 처리해보자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[상태를 React 안에서 관리하지 않고, 외부에서 External Store 라는 형태로 관리하는 법에 대해 알아보자]]></title>
            <link>https://velog.io/@jeong_lululala/external-store</link>
            <guid>https://velog.io/@jeong_lululala/external-store</guid>
            <pubDate>Mon, 25 Sep 2023 07:11:21 GMT</pubDate>
            <description><![CDATA[<h1 id="키워드">키워드</h1>
<ul>
<li>관심사의 분리</li>
<li>Layered Architecture</li>
<li>Flux Architecture</li>
<li>useReducer</li>
<li>useCallback</li>
</ul>
<h1 id="최종-목표">최종 목표</h1>
<p>관심사의 분리에 따라 Store 를 사용해 상태관리를 해보자.</p>
<h1 id="현재-목표">현재 목표</h1>
<p>상태를 React 안에서 관리하지 않고, 외부에서 External Store 라는 형태로 관리하는 법에 대해 알아보자.</p>
<h1 id="관심사의-분리란--내가-알빠가-뭐야">관심사의 분리란? = 내가 알빠가 뭐야</h1>
<p>separation of concerns, 줄여서 Soc 라고 부른다.</p>
<p>하나의 프로그램은 작은 부품이 모여서 만들어진다. 우리는 이미 작은 컴포넌트를 합쳐서 더 큰 컴포넌트를 만드는 방식으로 개발하고 있다.</p>
<p>예를 들어, 다음과 같이 나타낼 수 있다. 이는 <code>기능</code> 을 위주로 나누었다.</p>
<ul>
<li>App<ul>
<li>Header</li>
<li>Main<ul>
<li>Greeting</li>
<li>Counter</li>
<li>Posts</li>
<li>PostForm<ul>
<li>TextField</li>
</ul>
</li>
</ul>
</li>
<li>Footer</li>
</ul>
</li>
</ul>
<p>이런 식으로 나누는 이유는 무엇일까?</p>
<p>서로의 <code>관심사</code> 가 다르다. </p>
<p>예를 들어, <code>App</code> 에서는 <code>TextField</code> 가 어떻게 동작하는지 알 필요가 없다.</p>
<p>각 부분은 하나의 역할, 하나의 관심사로 격리 됨으로써, 복잡도를 낮추게 된다.</p>
<h1 id="관심사-분리의-관점은">관심사 분리의 관점은?</h1>
<p>위에서 처럼 <code>기능</code> 관점에서 볼 수도 있고, <code>설계</code> 관점, <code>프로세스</code> 관점에서도 볼 수 있다.</p>
<ul>
<li>기능 관점</li>
<li>설계 관점 (Layered Architecture)</li>
</ul>
<p>사용자에서 가까운 것과 먼 것으로 구분한다.</p>
<p>예를 들어,
가장 가까운 건 UI 를 다루는 부분, 
그 다음에는 Business 를 다루는 부분, 
그 너머에는 데이터에 접근하고 저장하는 부분으로 나눌 수 있다.</p>
<ul>
<li>프로세스 관점</li>
</ul>
<p>Input → Process → Output </p>
<p>거대한 프로그램이 아니라고 해도, 흔히 이렇게 3단계로 코드를 적절히 구분만 하면,
코드를 이해하고 유지보수하는데 크게 도움이 된다.</p>
<p>하나의 Output은 다시 사용자에게 Input을 요청하게 되고, 일반적인 프로그램은 다음과 같이 계속 순환하는 구조가 된다.</p>
<ol>
<li>Input: 프로그램 시작</li>
<li>Process: 프로그램 초기화</li>
<li>Output: 사용자에게 초기 UI 보여주기</li>
<li>Input: 사용자의 입력</li>
<li>Process: 사용자의 입력에 따라 처리</li>
<li>Output: 처리 결과 보여주기</li>
<li>Input: 사용자의 또 다른 입력</li>
<li>…반복…</li>
</ol>
<p>만약 Input Process 가 묶여 있는 구조라고 생각해보자. Process 부분을 테스트 하고 싶은데, I/O 가 섞여 있으면 복잡해진다. Process 부분이 따로 나와 있으면, 단위 테스트를 할 수 있어서 테스트 하기가 쉬워진다.</p>
<p>(물론 E2E 테스트를 배웠기 때문에 I/O 테스트도 가능하다.)</p>
<h1 id="flux-architecture">Flux Architecture</h1>
<p>MVC 에 대한 대안이다. </p>
<p>단방향 데이터 Flow 를 강조한다.</p>
<ol>
<li><code>Action</code>
이벤트/메시지 같은 객체이다.</li>
<li><code>Dispatcher</code>
(여러) Store로 Action을 전달한다. 메시지 브로커와 유사하다.</li>
<li><code>Store</code> (여러 개)
받은 Action에 따라 상태를 변경한다. Store 를 구독하고 있는 View 에게 상태 변경을 알린다.</li>
<li><code>View</code>
Store의 상태를 반영한다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/be7b9630-8757-4af5-864c-5dafcd189ab4/image.png" alt=""></p>
<h2 id="redux">Redux</h2>
<p>이것을 배경으로 하는 Redux는 <strong>단일 Store를 사용함</strong>으로써
좀 더 단순한 그림을 제안한다.</p>
<ol>
<li><code>Action</code></li>
<li><code>Store</code>
Store의 dispatch를 통해 Action을 받고, 
사용자가 정의한 reducer를 통해 State를 변경한다.</li>
<li><code>View</code>
State를 반영한다.</li>
</ol>
<p>여기서 <code>State를 변경한다</code> 의 의미는 
기존의 State 를 고치지 않고, 새로운 State 를 만든다는 것이다.</p>
<pre><code class="language-js">const state = { name: &#39;tester&#39; }

→ state.name = &#39;New Name&#39; (X)
→ const nextState = { ...state, name: &#39;New Name&#39; } (O)</code></pre>
<p>3단계를 거칠게 매칭해보자.</p>
<ul>
<li>Input → Action + dispatch</li>
<li>Process → reducer (사용자가 정의한 reducer를 통해 State를 변경한다.)</li>
<li>Output → View (React 가 상태를 UI 에 반영한다.)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/0bbcb8c4-5686-4a09-9d47-87c735809008/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/e7f36634-1d68-4a17-bb68-ade02b4a7259/image.png" alt=""></p>
<h1 id="external-store-란">External Store 란?</h1>
<p>지금까지는 상태를 useState 같은 Hook 을 이용해서 처리했다. 이제 다르게 처리해보자.</p>
<p>먼저 External Store 의 뜻은 무엇일까?</p>
<p>External: <code>React</code> 의 바깥 부분을 의미한다.
→ External Store: Store 가 React 의 안에 있지 않다 를 의미한다.</p>
<blockquote>
<p>Architecture 관점에서 말하는 <code>바깥 부분</code> 이 아니다.
따지고 보면 가장 바깥 부분은 UI 이다. 즉, React 가 바깥 부분에 있다.
여기서는 말하는 <code>바깥 부분</code> 은 React 입장에서 <code>바깥 부분</code> 을 말한 것이다.</p>
</blockquote>
<h1 id="상태-관리">상태 관리</h1>
<h2 id="usestate-로-상태-관리하기">useState 로 상태 관리하기</h2>
<p>Increase 버튼을 누르면 count 가 1씩 증가한다. 화면에도 증가되는 상태 값이 보인다.</p>
<pre><code class="language-tsx">import { useState } from &#39;react&#39;;

export default function Counter() {
  const [count, setCount] = useState(0);

  const handleClick = () =&gt; {
    setCount(count + 1);
  };

  return (
    &lt;div&gt;
      &lt;p&gt;{count}&lt;/p&gt;
      &lt;button type=&quot;button&quot; onClick={handleClick}&gt;
        Increase
      &lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<h2 id="usestate-없이-상태-관리하기-문제-발생">useState 없이 상태 관리하기: 문제 발생</h2>
<p>다음과 같이 작성하면 상태가 바뀌어도 화면에 적용되지 않는 문제가 발생한다.</p>
<pre><code class="language-tsx">import { useState } from &#39;react&#39;;

let count = 0;

export default function Counter() {

  const handleClick = () =&gt; {
    count += 1;

    console.log(count);
  };

  return (
    &lt;div&gt;
      &lt;p&gt;{count}&lt;/p&gt;
      &lt;button type=&quot;button&quot; onClick={handleClick}&gt;
        Increase
      &lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<h2 id="usestate-없이-상태-관리하기-forceupdate">useState 없이 상태 관리하기: forceUpdate</h2>
<p>Class 컴포넌트를 쓰던 시절에는 이렇게 사용했다. 강제로 리렌더링을 했다.</p>
<pre><code class="language-tsx">import { useReducer } from &#39;react&#39;;

let count = 0;

export default function Counter() {
  const [ignored, forceUpdate] = useReducer((x) =&gt; x + 1, 0);

  const handleClick = () =&gt; {
    count += 1;

    // 강제로 렌더링
    forceUpdate();
  };

  return (
    &lt;div&gt;
      &lt;p&gt;{count}&lt;/p&gt;
      &lt;button type=&quot;button&quot; onClick={handleClick}&gt;
        Increase
      &lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<h2 id="custom-hook-으로-분리">Custom Hook 으로 분리</h2>
<p>Custom Hook 으로 분리했더니, 상태를 React 가 관리하지 않게 됐다.</p>
<p>이런 식으로 만드는게 External Store 의 기본적인 아이디어이다.</p>
<pre><code class="language-tsx">import { useState } from &#39;react&#39;;

let count = 0;

function useForceUpdate() {
  const [state, setState] = useState(0);

  const forceUpdate = () =&gt; {
    setState(state + 1);
  };

  return forceUpdate;
}

export default function Counter() {
  const forceUpdate = useForceUpdate();

  const handleClick = () =&gt; {
    count += 1;

    forceUpdate();
  };

  return (
    &lt;div&gt;
      &lt;p&gt;{count}&lt;/p&gt;
      &lt;button type=&quot;button&quot; onClick={handleClick}&gt;
        Increase
      &lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<h2 id="함수가-항상-같게-된다-usecallback">함수가 항상 같게 된다, useCallback</h2>
<p>함수를 항상 같게 만들 수 있다.</p>
<p>왜 함수를 같게 만들어야 할까? </p>
<p>나중에 <code>useEffect(무언가를 처리, [forceUpdate]);</code> 를 해주기 위해서이다.
forceUpdate 가 바뀌가 되었을 때 무언가를 처리해주기 위해서이다.</p>
<pre><code class="language-tsx">// hooks/useForceUpdate.ts
import { useState, useCallback } from &#39;react&#39;;

export default function useforceUpdate() {
  const [, setState] = useState({});
  return useCallback(() =&gt; setState({}), []);
}</code></pre>
<pre><code class="language-tsx">// components/Counter.tsx
import useForceUpdate from &#39;../hooks/useForceUpdate&#39;;

let count = 0;

export default function Counter() {
  const forceUpdate = useForceUpdate();

  const handleClick = () =&gt; {
    count += 1;

    forceUpdate();
  };

  return (
    &lt;div&gt;
      &lt;p&gt;{count}&lt;/p&gt;
      &lt;button type=&quot;button&quot; onClick={handleClick}&gt;
        Increase
      &lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<h1 id="결론">결론</h1>
<p>이런 접근을 잘 하면, React 가 UI 를 담당하고, 순수한 TypeScript (또는 JavaScript) 가 비즈니스 로직을 담당하는, 관심사의 분리(Separation of Concerns) 를 명확히 할 수 있다. </p>
<p>자주 바뀌는 UI 요소에 대한 테스트 대신, 오래 유지되는 (바뀌면 치명적인) 비즈니스 로직에 대한 테스트 코드를 작성해 유지보수에 도움이 되는 테스트 코드를 치밀하게 작성할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/bd609318-7baf-4f2d-ad85-4848f4f9c603/image.png" alt=""></p>
<h1 id="다음에는">다음에는</h1>
<p>Store 라는 개념을 사용해보자.</p>
<p>TSyringe 와 TSyringe로 구축된 Store 를 이용해서 Redux 를 만들어보자.</p>
<p>다음에는 관심사의 분리가 왜 좋은지 더 와닿을 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[부담없이 천천히 하면 돼]]></title>
            <link>https://velog.io/@jeong_lululala/backend-survive-2</link>
            <guid>https://velog.io/@jeong_lululala/backend-survive-2</guid>
            <pubDate>Sun, 24 Sep 2023 14:01:20 GMT</pubDate>
            <description><![CDATA[<p>프론트 코스에서도 REST API 에 대해 배웠다. 그때는 추상적으로 느껴지는 부분이 많았는데, 이번 주차에서 실습과 함께 다루게 더 되면서 조금 더 이해하게 되었다.</p>
<p>REST API 를 이야기 할 때, Collection Pattern 이 빠지질 않았다. 창시자인 로이 필딩이 그렇게 되도록 명시한게 아니라 업계 통용으로 Collection Pattern 을 따른다는 점이 흥미로웠다.</p>
<p>하나의 URL 을 HTTP Method 만 다르게 해서 표현할 수 있다는 점, 일반적으로 복수로 표기한다는 점, URL 이 어떤 의미를 나타내야 하는지 등. Collection Pattern 을 쓰는, 적용하는 방법에 대해서 배웠다.</p>
<p>그리고 Spring Web MVC 로 구현을 해봤다. </p>
<p>Node.js 보다 코드를 체계적으로 작성하는 느낌이었다.
Request Mapping 이라던가 다양한 기능을 사용해서 편하게 작성할 수 있었다. 1주차에서 소켓을 만들었던 걸 생각하면 Spring 이 내부적으로 많은 일을 처리하고 있음을 느낀다.</p>
<p>다음에는 JSON 을 다룬다고 하는데 어느 부분을 간편하게 또 바꿀 수 있을 지 기대가 된다.</p>
<hr>
<p>요즘 학교, 알바, 졸업작품, 영어 공부, 부트캠프. 너무 많은 일을 하고 있다.
다 가져가고 싶은데 마음처럼 되지 않는다. 특히 부트캠프 진도를 프론트 때처럼 완벽하게 따라기가 쉽지 않다. 그래서 내린 결론은 &#39;천천히 부담없이 완주만 하자&#39;, &#39;어떤 식으로 백엔드가 돌아가는지 감만 잡자&#39;이다. 매주 많은 부담이 되긴 하지만 잘 견뎌보자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이론으로 배운 세상은 세상과 너무 달라서 ]]></title>
            <link>https://velog.io/@jeong_lululala/backend-survive-1</link>
            <guid>https://velog.io/@jeong_lululala/backend-survive-1</guid>
            <pubDate>Sun, 17 Sep 2023 14:21:57 GMT</pubDate>
            <description><![CDATA[<p>참 되는 게 없는 빠른 한 주를 보냈다. 텔렌트 코드를 읽으며 학습 방법에 대한 깨달음을 얻었는데 실제 생활에서 적용하는 건 정말 어려웠다. </p>
<p>수영을 시작한지 2주가 지났다. 다들 자유형을 되게 빨리 배운다. 내가 남들에 비해 뒤쳐질 게 뭐하나 없는데, 나만 아직 자유형을 하지 못한다. 이론적으로 오른쪽으로 고개를 얕게 빼면서 뒤를 보면 된다는 사실은 이해했지만, 고개를 오른쪽으로 돌리는 순간 코에 물이 들어가서 숨이 막힌다. 나중에 &#39;아하!&#39; 라고 외치는 순간이 올까. 못하는 부분을 천천히 반복 학습하면 된다는 텔런트 코드의 학습 방법을 적용하려고 해도 몸이 따라주지 않으면 어떻게 하라는 말인가.</p>
<p>메가테라를 다시 듣게 됐다. 이번엔 백엔드이다. 프론트를 듣고 너무 많이 배웠기 때문에 이 여정에 다시 올라섰다. 그리고 몇 개월만에 잊었던 고통을 다시 느꼈다. 메가테라 강의는 5분만 들어도 그 안에 든 내용이 엄청 많다. 강사님이 너무 당연한 듯 설명하시지만, 나에게는 전부 새로운 내용이다. 하나하나 정리하다보면 1시간 강의를 5시간 동안 듣게 된다. 흐름을 잡기 위해서도 굉장히 노력해야 하는데, 여기에도 잔꾀를 쓸 수 있는 방법은 없다. 무슨 방법도 통하지 않는 다는 것을 진작 깨달았다. 그저 천천히 머리가 아파도 정리하는 방법밖에 없다.</p>
<p>지금은 정말 괴롭다. 전체적인 흐름을 잡을 수 없는 시기이다. 그래도 프론트를 하고 느꼈는데, 끝나고 나면 그림이 잘 그려진다. 강의 내용을 2배속으로 들어도 쉽게 느껴진다. 백엔드 부트캠프도 마찬가지가 될 거라고 생각한다. 그저 꾸준히 가보자. 잘할 걸 알고 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ HTTP Server 를 가볍게 구현해보자]]></title>
            <link>https://velog.io/@jeong_lululala/HTTP-Server</link>
            <guid>https://velog.io/@jeong_lululala/HTTP-Server</guid>
            <pubDate>Sun, 17 Sep 2023 07:47:00 GMT</pubDate>
            <description><![CDATA[<h1 id="키워드">키워드</h1>
<ul>
<li>Java ServerSocket</li>
<li>Blocking vs Non-Blocking</li>
</ul>
<h1 id="최종-목표">최종 목표</h1>
<p>HTTP 에 대해서 간단히 알아보고 직접 구현해보자.</p>
<blockquote>
<p>웹의 핵심 요소인 HTTP는 웹 개발자에게 있어서 필수적으로 알아야 하는 지식이다. 그러나 상당수의 개발자들이 HTTP가 어떠한 원리로 동작하는지 제대로 알지 못하고 있다. 이번 과정을 통해 HTTP 통신을 처리하는 웹 서버가 의외로 간단한 원리로 구현되어 있다는 걸 알게 될 것이다. 직접 간단한 웹 서버를 구현해보면서, HTTP를 제대로 이해하고 동시에 Spring Web MVC가 어떤 편의를 제공하는지 절실하게 느껴보자.</p>
</blockquote>
<h1 id="현재-목표">현재 목표</h1>
<p> HTTP Server 를 가볍게 구현해보자.</p>
<h1 id="http-서버를-간단하게-만들어보자">HTTP 서버를 간단하게 만들어보자</h1>
<p>세팅은 HTTP Client 에서 했던 것과 동일하게 해준다.</p>
<blockquote>
<ol>
<li>gradle init</li>
<li>AppTest.java 내용을 날린다.</li>
<li><code>설정 &gt; tools</code> 에서 on Save 맨 위에 2개를 체크 표시로 바꿔준다. </li>
<li><code>구성 편집</code> 에서 app:run 을  Run Server 로 잡는다. </li>
</ol>
<p>이는 프로젝트마다 초기화 되기 때문에 매번 해줘야 한다.</p>
</blockquote>
<hr>
<p>HTTP 서버는 1, 2, 4, 5 단계를 처리하면 된다.</p>
<blockquote>
<p>1단계: 서버는 접속 요청을 받기 위한 소켓을 연다. → Listen
3단계: 서버는 접속 요청을 받아서 클라이언트와 통신할 소켓을 따로 만든다. → Accept
4단계: 소켓을 통해 서로 데이터를 주고 받는다. → Send &amp; Receive ⇒ 반복!
5단계: 통신을 마치면 소켓을 닫는다. → Close ⇒ 상대방은 Receive로 인지할 수 있다.</p>
</blockquote>
<h1 id="1️⃣-listen">1️⃣ Listen</h1>
<p>이는 다른 곳에 접속하는 게 아니기 때문에 포트 번호만 정하면 된다. (&#39;이 포트 번호로 요청을 줘&#39;) 만약 80 포트를 사용 중이라면 8080 등 다른 포트 번호를 쓰면 된다.</p>
<pre><code class="language-java">int port = 8080;</code></pre>
<hr>
<p>그리고 Java에서는 Socket 을 여는데 ServerSocket이라는 별도의 클래스를 사용한다.</p>
<p>ServerSocket 은 그냥 Socket 과 다르게 Send, Recive 를 안 할 것이기 때문에 별로의 Server Socket 을 만들어준 것이다. 그래서 Socket을 상속한 게 아니라, 완전히 구별된다는 점을 주의해야 한다. 
(내부를 보면 Socket 을 상속받지 않고 바로 java.io.Closeable 을 상속받는다.)</p>
<p>backlog 는 요청이 여러 번 들어올 때 얼마나 대기를 세울 지이다. </p>
<p>한 줄로 Listen 처리가 된다.</p>
<pre><code class="language-java">ServerSocket listener = new ServerSocket(port, backlog); // backlog 의 기본은 50이다.

ServerSocket listener = new ServerSocket(port, 0); </code></pre>
<hr>
<p>서버는 클라이언트와 다르게 Listen 되고 있는 상태에서 Accept 를 여러 번 할 수 있다.</p>
<p>Accpt 는 클라이언트의 접속을 기다린다. 클라이언트가 접속하면 통신용 소켓을 따로 만들어서 돌려준다.</p>
<pre><code class="language-java">Socket socket = listener.accept();</code></pre>
<p>연결을 하면 한 번 받아주고 바로 끊기게 된다. 그리고 기다리게 된다.</p>
<p>I/O에서 이렇게 기다리는 걸 Blocking이라고 한다. 파일 읽기, 쓰기 등도 모두 Blocking 동작이지만, TCP 통신에서는 네트워크 상태 같은 요인에 의해 크게 지연될 수 있고, accept처럼 상대방의 요청이 없으면 영원히 기다리는 (Blocking) 일이 벌어질 수 있다. 그래서 멀티스레드나 비동기, 이벤트 기반 처리 등이 필요하다. (이에 대해 지금은 다루지 않겠다.)</p>
<h1 id="2️⃣-request">2️⃣ Request</h1>
<p>서버 입장에서 Request 는 요청을 하는 게 아니라, 요청을 받는 것이다. 따라서 먼저 읽어야 한다.</p>
<p>코드는 클라이언트에서 다룬 것과 100% 동일하다.</p>
<pre><code class="language-java">InputStream inputStream = socket.getInputStream();

Reader reader = new InputStreamReader(inputStream);

CharBuffer charBuffer = CharBuffer.allocate(1_000_000);

reader.read(charBuffer);

charBuffer.flip();

String text = charBuffer.toString();

System.out.println(text);</code></pre>
<h1 id="3️⃣-response">3️⃣ Response</h1>
<p>클라이언트의 요청과 마찬가지로, 응답 메시지를 만들어서 전송하면 된다.</p>
<pre><code>HTTP/1.1 200 OK
(빈 줄)
Hello, world!</code></pre><hr>
<p>Java 코드</p>
<pre><code class="language-java">String message = &quot;&quot;&quot;
    HTTP/1.1 200 OK

    Hello, world!
    &quot;&quot;&quot;;</code></pre>
<p>또는</p>
<pre><code class="language-java">String message = &quot;&quot; +
    &quot;HTTP/1.1 200 OK\n&quot; +
    &quot;\n&quot; +
    &quot;Hello, world!\n&quot;;</code></pre>
<p>⚠️ 마지막 줄에 Newline(\n)을 넣는 걸 잊지 말자.</p>
<hr>
<p>제대로 하려면 Content-Type과 Content-Length를 더해주는 게 좋다.</p>
<pre><code class="language-java">String body = &quot;Hello, world!&quot;;
byte[] bytes = body.getBytes();
String message = &quot;&quot; +
    &quot;HTTP/1.1 200 OK\n&quot; +
    &quot;Content-Type: text/html; charset=UTF-8\n&quot; +
    &quot;Content-Length: &quot; + bytes.length + &quot;\n&quot; +
    &quot;\n&quot; +
    body;</code></pre>
<p>⚠️ Content-Length로 정확한 크기를 알 수 있기 때문에 마지막 줄에 Newline(\n)을 넣지 않아도 된다.</p>
<p>전송 코드는 클라이언트와 100% 동일하다.</p>
<h1 id="4️⃣-close">4️⃣ Close</h1>
<p>마찬가지로 클라이언트와 100% 동일하다.</p>
<h1 id="전체-코드">전체 코드</h1>
<pre><code class="language-java">package server;

import java.io.*;
import java.net.*;
import java.nio.*;

public class App {
    public static void main(String[] args) throws IOException {
        App app = new App();
        app.run();
    }

    private void run() throws IOException {
        // 1. Listen
        int port = 8080;

        ServerSocket listener = new ServerSocket(port, 0);

        System.out.println(&quot;Listen!&quot;);

        while (true) {

            // 2. Accept
            Socket socket = listener.accept();
            System.out.println(&quot;Accept!&quot;);

            // 3. Request
            InputStream inputStream = socket.getInputStream();
            Reader reader = new InputStreamReader(inputStream);

            CharBuffer charBuffer = CharBuffer.allocate(1_000_000);

            reader.read(charBuffer);

            charBuffer.flip();

            String text = charBuffer.toString();

            System.out.println(text);

            // 4. Response
            String body = &quot;Hello, world!&quot;;
            byte[] bytes = body.getBytes();
            String message = &quot;&quot; +
                    &quot;HTTP/1.1 200 OK\n&quot; +
                    &quot;Content-Type: text/html; charset=UTF-8\n&quot; +
                    &quot;Content-Length: &quot; + bytes.length + &quot;\n&quot; +
                    &quot;\n&quot; +
                    body;

            Writer writer = new OutputStreamWriter(socket.getOutputStream());
            writer.write(message);
            writer.flush();

            // 5. Close
            socket.close();
        }
    }
}</code></pre>
<h1 id="아하-포인트">아하! 포인트</h1>
<p>HTTP 서버가 1, 2, 4, 5 단계를 어떻게 처리하는지 좀 감을 잡은 것 같다.</p>
<h1 id="다음에는">다음에는?</h1>
<p>Blocking 같은 부분도 편하게 처리할 수 있는 준비된 것들에 대해 알아보자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TCP/IP 통신에 대해 가볍게 알아보자 그리고 HTTP Client 를 가볍게 구현해보자]]></title>
            <link>https://velog.io/@jeong_lululala/tcp-ip-http-client</link>
            <guid>https://velog.io/@jeong_lululala/tcp-ip-http-client</guid>
            <pubDate>Fri, 15 Sep 2023 13:23:07 GMT</pubDate>
            <description><![CDATA[<h1 id="키워드">키워드</h1>
<ul>
<li>TCP/IP 통신</li>
<li>TCP와 UDP</li>
<li>Socket과 Socket API 구분</li>
<li>URI와 URL</li>
<li>호스트(host)<ul>
<li>IP 주소</li>
<li>Domain name</li>
<li>DNS</li>
</ul>
</li>
<li>포트(port)</li>
<li>path(경로)</li>
<li>Java text blocks</li>
<li>Java InputStream과 OutputStream</li>
<li>Java try-with-resources</li>
</ul>
<h1 id="최종-목표">최종 목표</h1>
<p>HTTP 에 대해서 간단히 알아보고 직접 구현해보자.</p>
<blockquote>
<p>웹의 핵심 요소인 HTTP는 웹 개발자에게 있어서 필수적으로 알아야 하는 지식이다. 그러나 상당수의 개발자들이 HTTP가 어떠한 원리로 동작하는지 제대로 알지 못하고 있다. 이번 과정을 통해 HTTP 통신을 처리하는 웹 서버가 의외로 간단한 원리로 구현되어 있다는 걸 알게 될 것이다. 직접 간단한 웹 서버를 구현해보면서, HTTP를 제대로 이해하고 동시에 Spring Web MVC가 어떤 편의를 제공하는지 절실하게 느껴보자.</p>
</blockquote>
<h1 id="현재-목표">현재 목표</h1>
<p>TCP/IP 통신에 대해 가볍게 알아보자. 그리고 HTTP Client 를 가볍게 구현해보자.</p>
<h1 id="tcpip-통신-이란">TCP/IP 통신 이란?</h1>
<p>TCP/IP 는 TCP 프로토콜을 의미하는 게 아니다. <a href="https://ko.wikipedia.org/wiki/%EC%9D%B8%ED%84%B0%EB%84%B7_%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C_%EC%8A%A4%EC%9C%84%ED%8A%B8">인터넷 프토토콜 스위트</a>를 의미한다. 그래서 인터넷 프로토콜 스위트에서 제일 많이 쓰이는 TCP 와 IP 의 이름을 따서 TCP/IP 로 이름 지은 것이다. </p>
<p>이미지를 보면 HTTP 는 TCP 와 IP 를 기반으로 올라간다.</p>
<blockquote>
<p>물론 최근에는 UDP 를 TCP 와 비슷하게 구현한 상태로 그 위에서 HTTP 3.0 을 구현하긴 했다. 
하지만 우리는 HTTP 1.1 로 가장 기본적인 것을 쓸 것이기 때문에 TCP 를 쓴다고 생각하면 된다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/37c04248-05fc-4574-893b-185a85c53fcd/image.png" alt=""></p>
<h1 id="전송-계층의-대표적인-프로토콜">전송 계층의 대표적인 프로토콜</h1>
<p>이전 게시글에서 다음과 같이 [전송 계층] (<a href="https://ko.wikipedia.org/wiki/%EC%A0%84%EC%86%A1_%EA%B3%84%EC%B8%B5)%EC%9D%84">https://ko.wikipedia.org/wiki/%EC%A0%84%EC%86%A1_%EA%B3%84%EC%B8%B5)을</a> 정리하고 넘어왔다.</p>
<ul>
<li>4계층 - 전송 계층 → TCP, UDP ⇒ Port number</li>
</ul>
<p>전송 계층에는 여러 프로토콜도 있지만 TCP, UPD 가 대표적인 프로토콜이다. 이에 대해 알아보자.</p>
<h2 id="tcp-와-udp-란">TCP 와 UDP 란?</h2>
<p>TCP 와 UDP 는 <strong>연결 여부</strong>를 가지고 비교할 수 있다. 연결을 하고 안하고의 차이로 특징이 나뉜다.</p>
<p>TCP 는 마치 전화기같다. 연결이 필요하고, 연결한 후에 여러 개의 메시지를 보내면 순서를 보장한다. 이는 전화를 연결해서 우리가 말을 하면 어순이 바뀌지 않고 순서대로 전달되는 것과 같다. 그리고 확실하게 도착하는 것을 보장해서 전달한다.</p>
<p>반대로 UCP 는 마치 편지같다. 연결을 하지 않고 데이터를 보낸다. 순서가 보장되지 않는다. 확실하게 도착하는 것을 보장해서 전달하지 않는다.</p>
<ul>
<li>TCP <ul>
<li>전화같다.</li>
<li>연결이 필요하다.</li>
<li>전달 및 순서를 보장한다. </li>
</ul>
</li>
<li>UDP <ul>
<li>편지같다.</li>
<li>연결하지 않고 데이터를 보낸다.</li>
<li>전달 및 순서를 보장하지 않는다.</li>
</ul>
</li>
</ul>
<p>그래서 웹에서 쓰려면 TCP 를 써야 한다. (물론 위에서 말했듯이 최근에는 UDP 를 TCP 처럼 구현하기도 한다.)</p>
<h1 id="전송-프로토콜을-쓰기-위해서-socket-이란">전송 프로토콜을 쓰기 위해서, Socket 이란?</h1>
<p>TCP 와 UDP 같은 프로토콜을 쓰러면 Socket 이 필요하다.</p>
<p>이때, Socket과 Socket API를 구분해야 헷갈리지 않는다. Socket API 안에 Socket 이 있는 것이다.</p>
<p>Socket 과 Socket API 에 대해 알아보자.</p>
<blockquote>
<p>비트코인은 프로토콜 이름인데, 이 프로토콜 안에서 쓰이는 코인 이름도 비트코인으로 불리고 있는 것과 비슷하다. 헷갈리지 않게 주의하자.</p>
</blockquote>
<h1 id="socket-과-berkeley-sockets-socket-api-란">Socket 과 Berkeley Sockets (Socket API) 란?</h1>
<ul>
<li><a href="https://ko.wikipedia.org/wiki/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC_%EC%86%8C%EC%BC%93">네트워크_소켓</a></li>
<li><a href="https://en.wikipedia.org/wiki/Berkeley_sockets">Berkeley_sockets</a></li>
</ul>
<p>Socket 과 Berkeley Sockets(Socket API) 에서 전자의 Socket 은 우리가 일반적으로 알고 있는 Socket 이다.</p>
<p>이 Socket 은 엔드 포인트(종착역)인데, 맞닿는 부분이라고 할 수 있다. 그래서 서버에서의 엔드 포인트와 클라이언트의 엔드 포인트가 연결된다. 이때, 엔드 포인트를 Berkeley Sockets(Socket API) 를 이용해서 연결 시킨다. Berkeley Sockets(Socket API) 는 Socket 을 프로그래밍하기 위한 API 이다. 그래서 Socket API 를 이용해서 Socket 을 다룬다고 할 수 있다.</p>
<p>Socket 에 대해 더 자세히 알아보자. </p>
<p>Socket은 기본적으로 파일과 유사하게 다룰 수 있다. 그래서 Socket 은 유닉스의 파일 디스크립터와 비슷하다고 볼 수 있다. (유닉스에서는 파일을 읽고 쓰고 닫는 것을 돕는 Handler 가 있다. 이를 파일 디스크립터 라고 한다.)</p>
<p>Java에서는 I/O에 대한 것들을 전부 통일한다. 예를 들어, 키보드 입력, 화면 출력, 파일 입출력 등 모두  Stream(Java 8에서 도입된 Stream API가 아님에 주의!)으로 다룰 수 있다.</p>
<h1 id="tcp-통신-순서는">TCP 통신 순서는?</h1>
<p>TCP 프로토콜을 Socket API 를 이용해서 쓰려고 한다.  근데 TCP 는 어떻게 통신할까? 이에 대해 먼저 알아보자.</p>
<ol>
<li>서버는 접속 요청을 받기 위한 소켓을 연다. → Listen</li>
<li>클라이언트는 소켓을 만들고, 서버에 접속을 요청한다. → Connect</li>
<li>서버는 접속 요청을 받아서 클라이언트와 통신할 소켓을 따로 만든다. → Accept</li>
<li>소켓을 통해 서로 데이터를 주고 받는다. → Send &amp; Receive ⇒ 반복!</li>
<li>통신을 마치면 소켓을 닫는다. → Close ⇒ 상대방은 Receive로 인지할 수 있다.</li>
</ol>
<hr>
<p>아래는 단계별로 부가 설명을 적어봤다.</p>
<p><strong>1단계:</strong> 서버는 &#39;접속 요청을 받기 위해서&#39; 소켓을 열고 기다리고 있는다. </p>
<p><strong>2단계:</strong> 클라이언트도 소켓을 만든다. 그리고 서버 소켓에 요청을 한다.</p>
<p><strong>3단계:</strong> <code>서버의 요청을 받는 소켓</code> 이 <code>클라이언트의 요청한 소켓</code> 의 접속 요청을 받는다. 그 다음에 <code>서버에서 해당 클라이언트와 통신할 소켓</code> 을 만들어서, <code>클라이언트의 요청한 소켓</code> 과 연결한다. </p>
<p>서버는 여러 클라이언트의 요청을 받아서 처리하기 때문에, <code>요청을 받는 소켓</code> 과 <code>해당 클라이언트와 통신할 소켓</code> 이 따로 필요하다.</p>
<p><strong>4단계:</strong> 1, 2, 3 단계는 4 단계를 위한 준비단계라고 할 수 있다.</p>
<p><strong>5단계:</strong> 서버와 클라이언트 둘 중 누구든 통신을 Close 할 수 있다.</p>
<hr>
<p>👇 단계를 말로만 하니까 복잡하다. 클라이언트만 봐보자. 👇</p>
<h1 id="http-클라이언트를-간단하게-만들어보자">HTTP 클라이언트를 간단하게 만들어보자</h1>
<p>HTTP 클라이언트를 간단하게 만들어보자.</p>
<p>HTTP 클라이언트는 2, 4, 5 단계를 처리하면 된다.</p>
<blockquote>
<p>2단계: 클라이언트는 소켓을 만들고, 서버에 접속을 요청한다. → Connect
4단계: 소켓을 통해 서로 데이터를 주고 받는다. → Send &amp; Receive ⇒ 반복!
5단계: 통신을 마치면 소켓을 닫는다. → Close ⇒ 상대방은 Receive로 인지할 수 있다.</p>
</blockquote>
<hr>
<p>이는 Java Gradle 이 세팅되어 있다는 전제로 진행하기 때문에 다음과 같이 세팅을 먼저 해준다.</p>
<pre><code class="language-bash">sojeongyoo@Sojeongui-MacBookAir http-client % gradle init

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 2

Select implementation language:
  1: C++
  2: Groovy
  3: Java
  4: Kotlin
  5: Scala
  6: Swift
Enter selection (default: Java) [1..6] 3

Generate multiple subprojects for application? (default: no) [yes, no] no

Select build script DSL:
  1: Kotlin
  2: Groovy
Enter selection (default: Kotlin) [1..2] 2

Select test framework:
  1: JUnit 4
  2: TestNG
  3: Spock
  4: JUnit Jupiter
Enter selection (default: JUnit Jupiter) [1..4] 4

Project name (default: http-client): client
Source package (default: client): com.ahastudio.http.client
Enter target version of Java (min. 7) (default: 18):
Generate build using new APIs and behavior (some features may change in the next


&gt; Task :init
To learn more about Gradle by exploring our Samples at https://docs.gradle.org/8.3/samples/sample_building_java_applications.html</code></pre>
<h1 id="1️⃣-connect">1️⃣ Connect</h1>
<p>호스트에 Connect 요청을 한다. 호스트는 IP 주소 또는 도메인 이름을 사용할 수 있다. 원래 도메인 이름을 쓰게 되면, 도메인의 경우 DNS 를 활용해서 IP 를 얻는 등 여러가지로 복잡하다. 하지만 다행히도 자동으로 내부적으로 알아서 처리해준다.</p>
<h2 id="1-ip-주소와-포트-번호만-알면-서버에-접속할-수-있다">1. IP 주소와 포트 번호만 알면, 서버에 접속할 수 있다</h2>
<p>우리는 IP 주소와 포트 번호만 알면, 서버에 접속할 수 있다. </p>
<p>IP 주소를 알면 해당 기기에 접근할 수 있고, </p>
<p>포트 번호를 알면 해당 기기가 여러 프로그램을 쓰고 있을 때 &#39;해당 포트 번호는 내가 처리할게 라고 하는 서버 프로그램&#39;을 찾을 수 있다.</p>
<p>HTTP의 기본 포트 번호는 80 이다.</p>
<pre><code class="language-java">int port = 80;</code></pre>
<p>2단계에서 클라이언트는 소켓을 만들고 서버에 요청한다고 했지만, 자바에서는 &quot;어차피 바로 연결할 것이기 때문에&quot; 객체 생성이지만 여기서 바로 서버에 접속 요청을 한다. 실패하면 ConnectException 예외 발생시킨다.</p>
<pre><code class="language-java">Socket socket = new Socket(host, port);</code></pre>
<h1 id="2️⃣-request">2️⃣ Request</h1>
<p>연결을 했으면 이제 요청을 해야 한다. 요청 메시지를 만들고, TCP로 전송하자.</p>
<h2 id="1-요청-메시지를-만들자">1. 요청 메시지를 만들자</h2>
<p>HTTP 요청 메시지는 앞에서 정리했듯이 다음과 같은 구조를 가진다.</p>
<blockquote>
<p>Start line
Headers
빈 줄
Body</p>
</blockquote>
<p>그래서 다음과 같은 형태로 요청 메시지를 만들자. 후자가 최신 자바의 문법 형태라서 더 많이 쓴다.</p>
<p>⚠️ 마지막에 빈 줄을 넣는 걸 잊으면 안 된다. ⚠️</p>
<pre><code class="language-java">GET http://example.com/ HTTP/1.1
(빈 줄)


또는

GET / HTTP/1.1
Host: example.com
(빈 줄)</code></pre>
<p>Java 코드로 바꾸면 다음과 같다.</p>
<pre><code class="language-java">String message = &quot;&quot;&quot;
    GET / HTTP/1.1
    Host: example.com

    &quot;&quot;&quot;;

또는

String message = &quot;&quot; +
    &quot;GET / HTTP/1.1\n&quot; +
    &quot;Host: example.com\n&quot; +
    &quot;\n&quot;;</code></pre>
<blockquote>
<p>&quot;&quot;&quot; 는 java 에서 여러 줄을 쓰기 위한 표현이다.</p>
<p>URL 을 작성할 때 &#39;example.com&#39; 이 아닌 &#39;example.com/&#39; 으로 작성하자.
끝에 &#39;/&#39; Path 가 생략되도 웹 브라우저에서 자동으로 붙여주지만, 원래 있어야 완전한 URL 이다.</p>
</blockquote>
<h2 id="2-tcp로-전송하자">2. TCP로 전송하자</h2>
<p>이제 HTTP 요청 메시지를 TCP 로 전송하면 된다.</p>
<p>어떻게 전송할 수 있냐면, 소켓에서 Output Stream을 얻어서 쓸 수 있다.</p>
<pre><code class="language-java">OutputStream outputStream = socket.getOutputStream();
outputStream.write(message.getBytes());</code></pre>
<blockquote>
<p>OutputStream 의 특징인데, 전송할 때 String 가 아닌 Byte Array 로 보내야 한다.</p>
</blockquote>
<hr>
<p>문자열을 직접 전송하고 싶다면 Writer를 쓰면 된다. <strong>(이렇게 하는 것을 추천)</strong></p>
<pre><code class="language-java">OutputStreamWriter writer = new OutputStreamWriter(socket.getOutputStream());

writer.write(message);
writer.flush();</code></pre>
<blockquote>
<p>⚠️ 내부적으로 버퍼가 있기 때문에 write 를 하면 버퍼에 계속 쌓인다. flush를 이용해서 지워야 한다. flush 를 잊지 말고 사용하자.</p>
</blockquote>
<h1 id="3️⃣-response">3️⃣ Response</h1>
<p>접속했고 요청했으면, 이제 응답을 받아야 한다.</p>
<h2 id="2-응답을-받아-보자">2. 응답을 받아 보자</h2>
<p>어떻게 응답을 받을 수 있냐면, 소켓에서 Input Stream을 얻어서 쓸 수 있다.</p>
<pre><code class="language-java">InputStream inputStream = socket.getInputStream();</code></pre>
<p>참고로 Stream 의 특징은 다음과 같다.</p>
<p>Byte 배열을 준비하고, 여기로 데이터를 읽어온다. 만약, 응답 데이터가 우리가 준비한 배열보다 크다면, 반복해서 여러 번 읽는 작업이 필요하다. 이때 준비한 배열을 Chunk(한번에 처리하는 단위)라고 한다.</p>
<p>일단, 지금은 단순하게 하기 위해 여기서는 엄청 큰 배열을 준비해서 한번만 읽어보자.</p>
<pre><code class="language-java">byte[] bytes = new byte[1_000_000];
int size = inputStream.read(bytes); // 얼마나 읽었는지 값을 리턴해준다.</code></pre>
<p>1_000_000 Byte 를 잡았는데, 분명 다 쓰지 않았을 것이다. 실제 데이터 크기만큼 Byte 배열을 자르고, 문자열로 변환해 출력하자.</p>
<pre><code class="language-java">byte[] data = Arrays.copyOf(bytes, size); // size 만큼 잘라진 게 나온다.
String text = new String(data);

System.out.println(text);</code></pre>
<p>요청과 Write 와 마찬가지로, 응답에서는 문자열 처리를 위해서 Reader를 쓰면 훨씬 편하다 <strong>(이렇게 하는 것을 추천)</strong></p>
<pre><code class="language-java">InputStreamReader reader = new InputStreamReader(socket.getInputStream());

CharBuffer charBuffer = CharBuffer.allocate(1_000_000);

reader.read(charBuffer);

charBuffer.flip();

System.out.println(charBuffer.toString());</code></pre>
<blockquote>
<p>CharBuffer.allocate(1_000_000) 는 Char Buffer 를 미리 1_000_00 을 할당해주는 것이다.</p>
<p>⚠️ 그리고 toString 으로 CharBuffer 를 읽기 전에 flip 하는 것을 잊지 않아야 한다. ⚠️</p>
</blockquote>
<h1 id="4️⃣-close">4️⃣ Close</h1>
<h2 id="직접-close-하기">직접 Close 하기</h2>
<p>더이상 할 게 없으니 이제 close하면 된다.</p>
<pre><code class="language-java">socket.close();</code></pre>
<h2 id="자동-close-하기">자동 Close 하기</h2>
<p>만약 직접 Close 를 하고 싶지 않다면, try-with-resources를 써도 된다. </p>
<p>try 블록을 나가면 자동으로 Close 가 된다.</p>
<pre><code class="language-java">try (Socket socket = new Socket(host, port)) {
    // Request
    // Response
}</code></pre>
<h1 id="전체-코드">전체 코드</h1>
<pre><code class="language-java">package com.ahastudio.http.client;

import java.io.*;
import java.net.*;
import java.nio.*;

public class App {
    public static void main(String[] args) throws IOException {

        App app = new App();
        app.run();
    }

    private void run() throws IOException {
        System.out.println(&quot;Hello World&quot;);

        // 1. Connect
        try (Socket socket = new Socket(&quot;example.com&quot;, 80)) {

            System.out.println(&quot;Connect!&quot;);

            // 2. Reqeust
            String message = &quot;&quot; +
                    &quot;GET / HTTP/1.1\n&quot; +
                    &quot;Host: example.com\n&quot; +
                    &quot;\n&quot;;

            OutputStream outputStream = socket.getOutputStream();
            Writer writer = new OutputStreamWriter(outputStream);

            writer.write(message);
            writer.flush();

            System.out.println(&quot;Request!&quot;);

            // 3. Response
            InputStream inputStream = socket.getInputStream();
            Reader reader = new InputStreamReader(inputStream);

            CharBuffer charBuffer = CharBuffer.allocate(1_000_000);

            reader.read(charBuffer);

            charBuffer.flip();

            String text = charBuffer.toString();

            System.out.println(text);
        }

        // 4. Close
        System.out.println(&quot;Complete!&quot;);
    }
}</code></pre>
<h1 id="실행-결과">실행 결과</h1>
<pre><code class="language-bash">10:53:35 AM: 실행 중 &#39;run&#39;...

&gt; Task :app:compileJava UP-TO-DATE
&gt; Task :app:processResources NO-SOURCE
&gt; Task :app:classes UP-TO-DATE

&gt; Task :app:run
Hello World
Connect!
Request!
HTTP/1.1 200 OK
Accept-Ranges: bytes
Age: 161705
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
Date: Sun, 17 Sep 2023 01:53:35 GMT
Etag: &quot;3147526947&quot;
Expires: Sun, 24 Sep 2023 01:53:35 GMT
Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
Server: ECS (laa/7AA2)
Vary: Accept-Encoding
X-Cache: HIT
Content-Length: 1256

&lt;!doctype html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Example Domain&lt;/title&gt;

    &lt;meta charset=&quot;utf-8&quot; /&gt;
    &lt;meta http-equiv=&quot;Content-type&quot; content=&quot;text/html; charset=utf-8&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot; /&gt;
    &lt;style type=&quot;text/css&quot;&gt;
    body {
        background-color: #f0f0f2;
        margin: 0;
        padding: 0;
        font-family: -apple-system, system-ui, BlinkMacSystemFont, &quot;Segoe UI&quot;, &quot;Open Sans&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif;

    }
    div {
        width: 600px;
        margin: 5em auto;
        padding: 2em;
        background-color: #fdfdff;
        border-radius: 0.5em;
        box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);
    }
    a:link, a:visited {
        color: #38488f;
        text-decoration: none;
    }
    @media (max-width: 700px) {
        div {
            margin: 0 auto;
            width: auto;
        }
    }
    &lt;/style&gt;    
&lt;/head&gt;

&lt;body&gt;
&lt;div&gt;
    &lt;h1&gt;Example Domain&lt;/h1&gt;
    &lt;p&gt;This domain is for use in illustrative examples in documents. You may use this
    domain in literature without prior coordination or asking for permission.&lt;/p&gt;
    &lt;p&gt;&lt;a href=&quot;https://www.iana.org/domains/example&quot;&gt;More information...&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;

Complete!

BUILD SUCCESSFUL in 468ms
2 actionable tasks: 1 executed, 1 up-to-date
10:53:35 AM: 실행이 완료되었습니다 &#39;run&#39;.</code></pre>
<h1 id="아하-포인트">아하! 포인트</h1>
<blockquote>
<p>Socket 프로그래밍을 처음 해봤다. Socket 프로그래밍보다 로우 레벨을 만지는 일은 없다고 하신다. 그럼 거의 밑바닥을 보게 된 것이다. 별거 없지만 어려웠다.</p>
<p>받은 HTML 내용을 그랙픽적으로 표현하면 웹 브라우저가 되고, 
파싱을 하면 크롤러가 되는 등. 여러가지로 쓸 수 있다는 점이 신기했다.</p>
</blockquote>
<h1 id="다음에는">다음에는?</h1>
<p>HTTP 서버도 직접 만들어보자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP 에 대해서 간단히 알아보자]]></title>
            <link>https://velog.io/@jeong_lululala/http-understanding</link>
            <guid>https://velog.io/@jeong_lululala/http-understanding</guid>
            <pubDate>Tue, 12 Sep 2023 14:53:02 GMT</pubDate>
            <description><![CDATA[<h1 id="키워드">키워드</h1>
<ul>
<li>HTTP(Hypertext Transfer Protocol)</li>
<li>HTTP와 HTTPS의 차이(TLS)</li>
<li>클라이언트-서버 모델</li>
<li>stateless와 stateful</li>
<li>HTTP Cookie와 HTTP Session</li>
<li>HTTP 메시지 구조<ul>
<li>HTTP 요청(Request)와 응답(Response)<ul>
<li>multipart/form-data</li>
</ul>
</li>
<li>HTTP 요청 메서드(HTTP request methods)<ul>
<li>멱등성</li>
</ul>
</li>
<li>HTTP 응답 상태 코드(HTTP response status code)<ul>
<li>리다이렉션</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1 id="최종-목표">최종 목표</h1>
<p>HTTP 에 대해서 간단히 알아보고 직접 구현해보자.</p>
<blockquote>
<p>웹의 핵심 요소인 HTTP는 웹 개발자에게 있어서 필수적으로 알아야 하는 지식이다. 그러나 상당수의 개발자들이 HTTP가 어떠한 원리로 동작하는지 제대로 알지 못하고 있다. 이번 과정을 통해 HTTP 통신을 처리하는 웹 서버가 의외로 간단한 원리로 구현되어 있다는 걸 알게 될 것이다. 직접 간단한 웹 서버를 구현해보면서, HTTP를 제대로 이해하고 동시에 Spring Web MVC가 어떤 편의를 제공하는지 절실하게 느껴보자.</p>
</blockquote>
<h1 id="현재-목표">현재 목표</h1>
<p>HTTP 에 대해서 간단히 알아보자.</p>
<h1 id="http-란">HTTP 란?</h1>
<ul>
<li><a href="https://developer.mozilla.org/ko/docs/Web/HTTP">HTTP - MDN</a></li>
</ul>
<p>다음은 MDN 에 적힌 HTTP 의 정의이다. 하나씩 알아보자.</p>
<blockquote>
<p>하이퍼텍스트 전송 프로토콜(HTTP)은 HTML과 같은 하이퍼미디어 문서를 전송하기 위한 <strong>애플리케이션 레이어 프로토콜</strong>입니다. </p>
<p>웹 브라우저와 웹 서버간의 통신을 위해 설계되었지만 다른 목적으로도 사용할 수 있습니다. </p>
<p>HTTP는 클라이언트가 요청을 하기 위해 연결을 연 다음 응답을 받을때 까지 대기하는 전통적인 <strong>클라이언트-서버 모델</strong>을 따릅니다. </p>
<p>HTTP는 <strong>무상태 프로토콜</strong>이며, 서버가 두 요청 간에 어떠한 데이터(상태)도 유지하지 않습니다.</p>
</blockquote>
<p>아래 개념 3개를 알아보면서 HTTP 에 대해 알아보자.</p>
<p>개념1: 애플리케이션 레이어 프로토콜
개념2: 클라이언트-서버 모델
개념3: 무상태 프로토콜</p>
<h1 id="프로토콜-이란">프로토콜 이란?</h1>
<p>규칙의 집합, 규약, 약속이다. 무언가를 전송할 때 어떤 방식으로 할 지에 대한 약속이다. </p>
<blockquote>
<p>우리가 우측 통행하는 이유도 프로토콜처럼 약속이기 때문이다. 약속에는 항상 생긴 이유가 있다. 우측 통행은 그냥 가면 부딪히기 때문에 생긴 것이다.</p>
</blockquote>
<h1 id="개념1-애플리케이션-레이어-란">개념1: 애플리케이션 레이어 란?</h1>
<p>애플리케이션 레이어는 여러 의미로 쓰이지만, 여기서는 OSI 7계층을 이용해서 설명하겠다.</p>
<h1 id="osi-7계층-이란">OSI 7계층 이란?</h1>
<p>우리가 네트워크로 통신을 할 때 이런 계층을 통해서 한다.</p>
<ul>
<li><ol start="7">
<li>응용 계층</li>
</ol>
</li>
<li><ol start="6">
<li>표현 계층</li>
</ol>
</li>
<li><ol start="5">
<li>세션 계층</li>
</ol>
</li>
<li><ol start="4">
<li>전송 계층</li>
</ol>
</li>
<li><ol start="3">
<li>네트워크 계층</li>
</ol>
</li>
<li><ol start="2">
<li>데이터 링크 계층</li>
</ol>
</li>
<li><ol>
<li>물리 계층</li>
</ol>
</li>
</ul>
<p>실제로 7개의 계층을 모두 적용하지는 않는다.</p>
<p>대부분 1, 2, 3, 4 계층까지만 하고 바로 7 계층으로 넘어간다.
우리는 2, 3, 4, 7 계층만 살펴보자. 1계층인 물리 계층은 정말로 기계에 대한 내용이기 때문이다.</p>
<h1 id="2-3-4-7-계층">2, 3, 4, 7 계층?</h1>
<ul>
<li>2계층 - 데이터 링크 계층 ⇒ MAC address</li>
<li>3계층 - 네트워크 계층 ⇒ IP address</li>
<li>4계층 - 전송 계층 → TCP, UDP ⇒ Port number</li>
<li>7계층 - 응용 계층 → HTTP 등<ul>
<li>HTTPS를 위한 TLS 같은 보안 계층이 먼저 들어갈 수도 있다.</li>
</ul>
</li>
</ul>
<p>기기를 사면 MAC Address 가 할당된다. (2계층)
인터넷을 연결하면 IP Address 가 할당된다. (3계층)
Port Number 는 프로그램에서 지정한다. (4계층)</p>
<p>2, 3, 4 계층은 기본적으로 주어지는 것들이다.</p>
<h1 id="2계층은">2계층은?</h1>
<ul>
<li>2계층 - 데이터 링크 계층 ⇒ MAC address</li>
</ul>
<p>2계층은 소프트웨처 측면에서 하드웨어를 추상화한 것이다.</p>
<p>기기 같은 것을 구분을 하기 위해서 MAC Address 를 쓴다.
그래서 MAC Address 는 사실상 하드웨어에서 얻는 것이다.</p>
<h1 id="3계층-이란">3계층 이란?</h1>
<ul>
<li>3계층 - 네트워크 계층 ⇒ IP address</li>
</ul>
<p>3계층에서는 IP Address 를 쓴다. (2계층에서 MAC Address 를 쓰는 것처럼)</p>
<p>IP 는 인터넷 프로토콜이다. 서로 멀리 떨어져 있는 애들끼리 서로를 인식하는 방법 중에 하나이다.</p>
<h1 id="4계층-이란">4계층 이란?</h1>
<ul>
<li>4계층 - 전송 계층 → TCP, UDP ⇒ Port number</li>
</ul>
<p>내 컴퓨터에는 여러 가지가 프로그램이 돌아갈 수 있다.</p>
<p>그래서 컴퓨터 대 컴퓨터를 연결이 아닌 프로그램 대 프로그램 연결을 얘기해보자.</p>
<p>두 프로그램을 연결해야 하는데, 먼저 IP Address 로 기기를 찾는다. 그리고 그 다음에 내 프로그램이 어떤 걸 쓴다는 것을 나타내기 위해서 Port Number 를 쓴다.</p>
<h1 id="7계층-이란">7계층 이란?</h1>
<ul>
<li>7계층 - 응용 계층 → HTTP 등<ul>
<li>HTTPS를 위한 TLS 같은 보안 계층이 먼저 들어갈 수도 있다.</li>
</ul>
</li>
</ul>
<p>2, 3, 4 계층을 적용하고 나머지는 건너 뛰어서 7계층으로 왔다. 7계층은 HTTP 같은 걸 의미한다. </p>
<p>2, 3, 4 계층은 기본적으로 주어진 느낌이라 인프라 개념인데, 7계층은 웹 브라우저라는 특별한 프로그램을 써서 직접 프로그램을 만든 것이다.</p>
<h1 id="7계층-https-란">7계층 HTTPS 란?</h1>
<ul>
<li>7계층 - 응용 계층 → HTTP 등<ul>
<li>HTTPS를 위한 TLS 같은 보안 계층이 먼저 들어갈 수도 있다.</li>
</ul>
</li>
</ul>
<p>HTTPS 는 보안 프로토콜이다. HTTPS 는 HTTP 와 별개로 따로 있는 게 아니다. TLS 하나를 거쳐서 HTTP 를 그대로 쓴다고 생각하면 된다. </p>
<p>이는 아래 사진을 보면 TLS 같은 보안 계층이 먼저 들어갈 수 있다.</p>
<h1 id="web-이란">Web 이란?</h1>
<p>우리가 WEB 이라고 부르는 것은 파란색 박스 부분을 제외한 부분이다. 즉, HTTP 부터 위에 것들이다. 이것들을 통틀어서 WEB 이라고 한다. 웹을 공부한다고 하면 범위가 이렇게 된다고 생각하면 된다.</p>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/37c04248-05fc-4574-893b-185a85c53fcd/image.png" alt=""></p>
<p>그리고 사진을 다시 보며, 구조에 대해 파악해보자.</p>
<p>IP 부터 시작한다. 우리가 어딘가에 접속할 때 MAC Address 를 쓰지 않는다. 내 MAC Adrress 로컬에서만 인지할 수 있고 원격에서 알 수 없다. 그래서 원격에서 알 수 있는 IP부터 시작하는 것이다.</p>
<p>IP가 있고 그 위에다가 TCP나 UDP를 얹을 수 있다. 그 다음에 DNS 같은 것들도 얹을 수 있다. </p>
<p>그래서 일단 기본적으로 IP에 TCP 를 얹고 그 위에 HTTP 가 올라간다고 생각하면 된다.</p>
<h1 id="개념2-클라이언트-서버-모델-이란">개념2: 클라이언트-서버 모델 이란?</h1>
<ul>
<li>서비스/리소스 → <a href="https://developer.mozilla.org/ko/docs/Web/HTTP/Basics_of_HTTP/Identifying_resources_on_the_Web">URL</a><ul>
<li>scheme://host:port/path?query#fragment</li>
</ul>
</li>
<li>클라이언트 → 요청</li>
<li>서버 → (처리) → 응답</li>
</ul>
<p>서비스 개념에서 보자. 클라이언트는 고객이고 서버는 종업원이다. 고객이 &quot;햄버거 하나 주세요&quot; 라는 요청을 보내면 종업원은 햄버거를 만들어서 준다. 이 과정은 요청, 처리(햄버거를 만드는 처리), 응답이 이루어진 것이다. </p>
<p>이때 처리는 명확히 나와야 한다. &quot;햄버거 하나 주세요&quot; 가 명확해지기 위해서 중요한 부분은 어디일까? 리소스 즉, &quot;햄버거&quot; 이다. </p>
<p>응답할 때 리소스를 얻어서 처리한다. 그리고 처리한 내용을 HTML 과 같은 하이퍼미디어 문서의 형태로 돌려준다.</p>
<p>리소스는 URL 을 이용해서 드러낼 수 있다.</p>
<h1 id="개념3-무상태-란">개념3: 무상태 란?</h1>
<p>HTTP는 각각의 요청이 독립적이다. 그래서 클라이언트는 항상 자신이 누구인지 알려줘야 한다.</p>
<p>무상태 특징에 대해 알아보자.</p>
<h1 id="특징1-http는-각각의-요청이-독립적이다">특징1: HTTP는 각각의 요청이 독립적이다</h1>
<p>HTTP는 각각의 요청이 독립적이다.</p>
<p>예를 들어서, 햄버거 가게에 가서 &quot;햄버거 주세요&quot; 를 말하고 &quot;콜라도 주세요&quot; 라고 말한다면, 내가 두 개를 같이 주문한 사람인 것을 알기 때문에 한 꺼번에 내어준다.</p>
<p>하지만 HTTP 는 그렇지 않다. 각각의 요청이 독립적이다. 한 꺼번에 받고 싶다면, &quot;저는 A 입니다. 햄버거 주세요&quot; 를 말하고 &quot;저는 A 입니다. 콜라를 주세요&quot; 라고 말해야 한다.</p>
<p>결국 항상 내가 누구인지 알려줘야 한다는 의미이다. </p>
<h1 id="특징2-그래서-클라이언트는-항상-자신이-누구인지-알려줘야-한다">특징2: 그래서 클라이언트는 항상 자신이 누구인지 알려줘야 한다</h1>
<p>HTTP는 각각의 요청이 독립적이다. 그래서 클라이언트는 항상 자신이 누구인지 알려줘야 한다.</p>
<p>그렇다면 내가 누구인지 유지시키면 되지 않을까? 어떤 방법을 쓸 수 있을까? 클라이언트가 자신이 누구인지 알려주는 방법은 다음과 같다.</p>
<h1 id="특징2-내가-누군인지-유지시키는-방법은">특징2: 내가 누군인지 유지시키는 방법은?</h1>
<h2 id="1-요청과-응답을-통해-계속-주고-받는-쿠키">1. 요청과 응답을 통해 계속 주고 받는 쿠키</h2>
<p>요청과 응답에 어떤 값들을 계속 넣으면 된다.</p>
<p>내가 어떤 값을 넣어서 서버에 요청한다. 그러면 서버에서는 요청받았던 값을 그대로 돌려주거나 조작해서 돌려준다.</p>
<h2 id="2-데이터는-서버에서-관리하고-쿠키-등으로-key를-관리하는-세션">2. 데이터는 서버에서 관리하고 쿠키 등으로 key를 관리하는 세션</h2>
<p>예를 들어보자. </p>
<p><strong>클라이언트:</strong> &quot;나 처음이야&quot;
<strong>서버:</strong> &quot;그럼 너는 123 이라고 부를게&quot;
<strong>서버:</strong> &quot;123 에 대한 데이터를 어딘가에 저장할게.&quot;</p>
<p><code>123</code> 은 클라이언트의 세션 키가 된다. 세션 자체는 서버에서 관리하지만, 세션에 대한 키는 대게로 쿠키에서 관리한다. </p>
<blockquote>
<p>그리고 위에서 말했듯이 쿠키는 요청할 때마다 계속 전달이 된다. 그래서 무겁다.
예전에는 쿠키를 이용하지 않고 URL에 넣기도 했다. 지금은 절대 이렇게 하지 않는다.</p>
</blockquote>
<h2 id="3-웹-브라우저의-기능-localstorage-등">3. 웹 브라우저의 기능 (localStorage 등)</h2>
<p>만약, 보편적인 리소스에 접근할 때도 내가 누군지 식별하는 게 필요할까? 그냥 가벼운 요청에서는 식별하지 않게 하는 방법은 없을까?</p>
<p>서버에서 관리되고 있는 데이터에 세션 키를 가지고 매칭해본다. 그리고 로컬 스토리지에 넣는다. 그러면 클라이언트가 독자적으로 데이터를 가질 수 있다.</p>
<h1 id="http-메시지-란">HTTP 메시지 란?</h1>
<p>클라이언트가 요청을 하면 그 요청에 대한 결과인 응답을 서버한테 받는다. 여기서 서로 주고 받는 것을 <code>메시지</code> 라고 한다. 서로 주고 받는 행위는 <code>트랜잭션</code> 이라고 한다.</p>
<p>HTTP 메시지의 특징은 다음과 같다.</p>
<ol>
<li>기본적으로는 사람이 읽을 수 있는 형태이다.</li>
<li>요청과 응답 모두 동일 구조이다.<ol>
<li>Start line → 요청과 응답의 형태가 다르다.</li>
<li>Headers</li>
<li>빈 줄</li>
<li>Body<ol>
<li>크기를 알기 어렵다. Headers의 Content-Length 항목 등을 활용한다.</li>
<li>위와 다르게 꼭 사람이 읽을 수 있는 텍스트 형태일 필요는 없다. 바이너리 등 가능.</li>
<li>하나가 아니라 여럿일 수도 있다. 파일 업로드 등을 위해 쓰이는 multipart/form-data가 대표적.</li>
</ol>
</li>
</ol>
</li>
</ol>
<h1 id="특징1-기본적으로는-사람이-읽을-수-있는-형태이다">특징1: 기본적으로는 사람이 읽을 수 있는 형태이다</h1>
<p>트랜잭션을 통해서 서로 메시지를 주고 받는다. 이때 HTTP 에서는 기본적으로 사람이 읽을 수 있는 형태로 주고 받게 되어있다.</p>
<p>HTTP 와 반대로 사람이 읽을 수 없을 형태를 주고 받는 예시를 생각해보자. </p>
<p>온라인 게임이 그 예시가 될 수 있다. 온라인 게임을 만들 때는 123 이라는 숫자를 보낼 때 바이너리 형태로 보낸다면 1바이트로 충분히 보낼 수 있다.</p>
<p>이렇게 퍼포먼스를 많이 요구하는데서는 이렇게 바이너리 형태를 많이 쓴다. 아니면 해킹을 막기 위한 목적으로 암호화를 해서 보내기도 한다.</p>
<p>하지만 기본적으로 HTTP 에서는 사람이 읽을 수 있는 형태를 지향한다. 사람이 읽을 수 있으면 디버깅도 쉽고 처리도 쉽다.</p>
<h1 id="특징2-요청과-응답-모두-동일-구조이다">특징2: 요청과 응답 모두 동일 구조이다</h1>
<p>HTTP 메시지는 요청할 때와 응답할 때를 비교했을 때 완전히 다른 모양으로 만들 수 있지만, </p>
<p>기본적인 구조는 동일하다. HTTP 메시지의 구조에 대해 알아보자.</p>
<h1 id="http-메시지의-구조-란">HTTP 메시지의 구조 란?</h1>
<p>HTTP 메시지의 구조는 총 4가지로 구성된다.</p>
<ol>
<li>Start line<ul>
<li>요청과 응답의 형태가 다르다.</li>
</ul>
</li>
<li>Headers</li>
<li>빈 줄</li>
<li>Body<ol>
<li>크기를 알기 어렵다. Headers의 Content-Length 항목 등을 활용한다.</li>
<li>위와 다르게 꼭 사람이 읽을 수 있는 텍스트 형태일 필요는 없다. 바이너리 등 가능.</li>
<li>하나가 아니라 여럿일 수도 있다. 파일 업로드 등을 위해 쓰이는 multipart/form-data가 대표적.</li>
</ol>
</li>
</ol>
<h1 id="body-의-특징은">Body 의 특징은?</h1>
<h1 id="특징1-크기를-알기-어렵다">특징1: 크기를 알기 어렵다</h1>
<p>Body 의 크기를 알기 어렵기 때문에 다 받았는지 알 수가 없다.</p>
<p>→ 그래서 Headers의 Content-Length 항목 등을 활용한다. Content-Length 항목의 크기만큼 받을 수 있다.</p>
<h1 id="특징2-위와-다르게-꼭-사람이-읽을-수-있는-텍스트-형태일-필요는-없다">특징2: 위와 다르게 꼭 사람이 읽을 수 있는 텍스트 형태일 필요는 없다</h1>
<p>Start line 과 Headers 는 사람이 읽을 수 있는 텍스트 형태이다. 하지만 Body 는 아닐 수 있다.</p>
<p>Start Line 과 Headers 는 사람이 읽을 수 있는 텍스트 형태인데, Body 만 바이너리 같은 형태로 가능하다.</p>
<p>→ 바이너리 등 가능</p>
<h1 id="특징3-하나가-아니라-여럿일-수도-있다">특징3: 하나가 아니라 여럿일 수도 있다</h1>
<p>→ 파일 업로드 등을 위해 쓰이는 multipart/form-data가 대표적</p>
<h1 id="http-method-란-요청">HTTP Method 란? (요청)</h1>
<p>&#39;주소에 대해 무엇을 할 지&#39;를 HTTP 메서드를 통해서 결정한다.</p>
<ol>
<li>GET → Read</li>
<li>HEAD → GET without body</li>
<li>POST → Submit (멱등성X) ⇒ Collection Pattern에서 Create로 사용</li>
<li>PUT → Update (+Create) ⇒ Overwrite!</li>
<li>PATCH → Update (partial) (멱등성X)</li>
<li>DELETE → Delete</li>
<li>OPTIONS → 지원 확인</li>
</ol>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/34a6be46-a672-405d-89a1-f6800c6e401f/image.png" alt=""></p>
<blockquote>
<p>이미지에서 Requests 부분에 </p>
<p>주소는 &#39;/&#39; 이다.
버전은 HTTP 1.1 이다
최신 버전이 존재하긴 우리가 일반적으로 쓰는 것은 1.1 기반이다.
2.0 을 쓰더라도 1.1 과 거의 동일한 형태를 고도화 시킨 것이다.</p>
</blockquote>
<h1 id="head-란">HEAD 란?</h1>
<ul>
<li>HEAD → GET without body</li>
</ul>
<p>body 없이 head 만 얻고 싶을 때가 있다.  예를 들어, 존재하지 않는 페이지를 갔을 때 존재하지 않는 다는 사실만 알면 된다. 이걸 Head 가 처리할 수 있다. 그래서 body 는 필요없다. </p>
<h1 id="post-란">POST 란?</h1>
<ul>
<li>POST → Submit (멱등성X)  ⇒ Collection Pattern에서 Create로 사용</li>
</ul>
<p>멱등성이란? 같은 것을 여러 번 해도 바뀌는 게 없는 것이다. </p>
<p>POST 는 멱등성을 보장하지 않는다. 회원가입을 예로 들어보자. 처음엔 됐지만 두 번째부터는 동일한 아이디가 있기 때문에 에러가 난다.</p>
<p>그리고 Collection Pattern 는 산업 표준처럼 쓰이는데 여기서 POST 는 &#39;모든 데이터를 보낸다&#39; 는 의미가 아니라 &#39;Create&#39; 의 의미로 제한을 건다.</p>
<h1 id="put-란">PUT 란?</h1>
<ul>
<li>PUT → Update (+Create) ⇒ Overwrite!</li>
</ul>
<p>PUT 은 Update 또는 Create 를 하는데 POST 가 Create 용도이다 보니 PUT 은 Update 로 많이 쓰인다.</p>
<p>PUT 에서 Update 는 일부만 고치는 게 아니라 전체를 고치는 Overwrite 
이다.</p>
<p>예를 들어보자. 회원 정보에서 이름을 수정하려고, PUT 으로 수정한 이름만 보냈다. 그러면 수정한 이름을 빼고 나머지 정보는 다 날아간다.</p>
<h1 id="patch-란">PATCH 란?</h1>
<ul>
<li>PATCH → Update (partial=부분적) (멱등성X)</li>
</ul>
<p>PATCH 는 나중에 추가된 HTTP Method 이다.</p>
<p>PATCH 는 PUT 과 다르게 부분적으로 Update 가 가능하다.</p>
<blockquote>
<p>물론 실제로 코딩을 하면 PUT 과 PATCH 를 비슷하게 만들 수 있다.
하지만 원론적인 이 둘의 차이점을 잘 기억해두자.</p>
</blockquote>
<h1 id="http-status-code-란-응답">HTTP Status Code 란? (응답)</h1>
<p>현재 URL 과 HTTP Method 을 이용해서 리소스를 어떻게 해달라는 요청이 왔다. 이제 어떻게 됐다라는 상태 코드를 보내주면 된다.</p>
<p>HTTP Status Code 는 세자리 수로 표현한다. 그리고 표준으로 정해져 있다.</p>
<ol>
<li>1xx → 정보<ul>
<li>우리가 직접 쓰는 일은 드믈다.</li>
<li>HTTP 완벽 가이드 읽으면 알 수 있다.</li>
</ul>
</li>
<li>2xx → 성공<ul>
<li><code>200 OK</code></li>
<li><code>201 Created</code> (POST 로 요청해서 성공했다.)</li>
<li><code>204 No Content</code> (성공했는데 body 가 없다.)</li>
</ul>
</li>
<li>3xx → 리다이렉션<ul>
<li><code>304 Not Modified</code> 가 특수한 형태로 자주 보인다.<ul>
<li>304 는 응답이 안 바뀌었을 때 나온다. </li>
<li>예를 들어, 캐쉬를 날리지 않고 새로 고침을 하면 304 가 나온다.</li>
</ul>
</li>
</ul>
</li>
<li>4xx → 클라이언트 쪽 문제<ul>
<li>404 Not Found</li>
</ul>
</li>
<li>5xx → 서버 쪽 문제<ul>
<li>500 Internal Server Error<ul>
<li>서버 내부 에러이다.</li>
</ul>
</li>
</ul>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/jeong_lululala/post/34a6be46-a672-405d-89a1-f6800c6e401f/image.png" alt=""></p>
<blockquote>
<p>이미지에서 Responses 를 보면 버전, HTTP Status Code, HTTP Status Message 이 나와있다.
HTTP Status Message 는 별로 중요하지 않다. 읽을 수 있지만 실제로 쓰는 값이 아니다.
예를 들어서 HTTP Status Code 가 200 로 왔는데, HTTP Status Message 가 &#39;Not Found&#39; 가 왔다. 웹 브라우저는 200d은 OK 이기 때문에 성공했다고 인지하고 성공으로 처리한다. 뒤에 Message 가 &#39;Not Found&#39; 건 뭐건 신경쓰지 않는다.</p>
</blockquote>
<h1 id="아하-포인트">아하! 포인트</h1>
<blockquote>
<p>HTTP 에 대해 처음 공부해봤다.
요청을 받았을 때, 에러에 따라서 적절한 처리를 해줄 수 있을 것 같다.
굉장히 기본적인 내용인데도 모르는 부분이 많아서 정리가 오래 걸렸다. 그래도 흐름을 놓치지 않고 따라가고 있어서 다행이다.</p>
</blockquote>
<h1 id="다음에는">다음에는?</h1>
<p>이 게시물의 내용을 코드로 구현해보자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Template 으로 개발 환경 세팅 하기]]></title>
            <link>https://velog.io/@jeong_lululala/template</link>
            <guid>https://velog.io/@jeong_lululala/template</guid>
            <pubDate>Tue, 12 Sep 2023 04:28:26 GMT</pubDate>
            <description><![CDATA[<h1 id="template-repo-란">Template Repo 란?</h1>
<p>Github 에 개발 환경 세팅이 완료된 Template 레포지토리를 만들어보자. 매번 프로젝트를 할 때마다 기본 세팅을 해야 하는 일을 없앨 수 있다.</p>
<h1 id="template-repo-사용하기">Template Repo 사용하기</h1>
<p>마치 Fork 를 하듯이 프로젝트를 바로 만들 수 있는데.</p>
<p>Template 레포지토리와 동일한 디렉터리 구조와 파일을 가진 새 레포지토리를 생성할 수 있다.</p>
<pre><code class="language-bash">git init
git remote add origin &lt;url&gt;
git fetch origin main
git switch -c main origin/main
rm -rf .git</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[usehook-ts 라이브러리에 대해 알아보자]]></title>
            <link>https://velog.io/@jeong_lululala/usehooks-ts</link>
            <guid>https://velog.io/@jeong_lululala/usehooks-ts</guid>
            <pubDate>Fri, 08 Sep 2023 07:22:09 GMT</pubDate>
            <description><![CDATA[<h1 id="키워드">키워드</h1>
<ul>
<li>usehooks-ts<ul>
<li>useBoolean</li>
<li>useEffectOnce</li>
<li>useFetch</li>
<li>useInterval</li>
<li>useEventListener</li>
<li>useLocalStorage</li>
<li>useDarkMode</li>
</ul>
</li>
<li>swr</li>
<li>react-query</li>
</ul>
<h1 id="최종-목표">최종 목표</h1>
<p>간단한 서버를 만들고, custom hook 을 사용하여 서버에서 데이터를 받아올 수 있다.</p>
<h1 id="현재-목표">현재 목표</h1>
<p>usehook-ts 라이브러리에 대해 알아보자.</p>
<h1 id="usehooks-ts-란">usehooks-ts 란?</h1>
<ul>
<li><a href="https://usehooks-ts.com/">usehooks-ts 공식문서</a></li>
</ul>
<p>타입스크립트로 만들었지만 자바스크립트에서도 사용 가능하다.</p>
<p>모든 Hook 에 대해 어떻게 만들었는지 나와있어서 Hook 을 만드는데 많은 영감을 얻을 수 있다.</p>
<h1 id="usehooks-ts-설치하기">usehooks-ts 설치하기</h1>
<pre><code class="language-ts">npm i usehooks-ts</code></pre>
<h1 id="useboolean-이란">useBoolean 이란?</h1>
<ul>
<li><a href="https://usehooks-ts.com/react-hook/use-boolean">useBoolean 공식문서</a></li>
</ul>
<p>참/거짓을 다룰 땐 toogle 같이 <strong>의도가 명확한 함수</strong>를 쓰는 게 좋다. </p>
<p><a href="https://velog.io/@jeong_lululala/react-hook">이전 포스트</a> 에서 작성했던 <code>TimerControl</code> 에 써보자.</p>
<p>setPlaying(!playing) 보다 toogle 혹은 tooglePlaying 이 의도가 명확하다.</p>
<pre><code class="language-tsx">export default function TimerControl() {
    // const [playing, setPlaying] = useState(false);
    const {value: playing, toogle: tooglePlaying} = useBoolean(); // 추가

      // const handleClick = () =&gt; {
    //    setPlaying(!playing);
    // };

      return (
        &lt;div&gt;
            {playing ? (
                  &lt;Timer /&gt;
            ) : (
                &lt;p&gt;Stop&lt;/p&gt;
            )}
            &lt;button type=&quot;button&quot; onClick={tooglePlaying}&gt;
                Toggle
            &lt;/button&gt;
        &lt;/div&gt;
    );
}</code></pre>
<h1 id="useeffectonce-이란">useEffectOnce 이란?</h1>
<ul>
<li><a href="https://usehooks-ts.com/react-hook/use-effect-once">useEffectOnce 공식문서</a></li>
</ul>
<p>의존성 배열을 빈 배열로 넣어서 한 번만 실행하는 Effect 를 잡아줄 때가 많은데, useEffectOnce 를 쓰면 더 명확히 드러난다. </p>
<p>로직은 똑같지만 이름을 명확히 했을 때 혜택을 무시할 수 없다.</p>
<p><a href="https://velog.io/@jeong_lululala/react-hook">이전 포스트</a> 에서 작성했던 <code>useFetchProducts</code> 에 써보자.</p>
<pre><code class="language-tsx">const [products, setProducts] = useState&lt;Product[]&gt;([]);

useEffectOnce(() =&gt; {
    const fetchProducts = async () =&gt; {
        const url = &#39;http://localhost:3000/products&#39;;
        const response = await fetch(url);
        const data = await response.json();
        setProducts(data.products);
    };

    fetchProducts();
}); // 의존성 배열이 필요없다.</code></pre>
<h1 id="usefetch-란">useFetch 란?</h1>
<ul>
<li><a href="https://usehooks-ts.com/react-hook/use-fetch">useFetch 공식문서</a></li>
</ul>
<p>정말 간단히 쓸 때 좋다.</p>
<pre><code class="language-tsx">export default function useFetchProducts() {
  const url = &#39;http://localhost:3000/products&#39;;

  //    const response = await fetch(url);
  //    const data = await response.json();
  const { data } = useFetch(url);

  if (!data) { return []; }
  return data.products;
}</code></pre>
<h1 id="use-http-란">use-http 란?</h1>
<ul>
<li><a href="https://use-http.com/#/">use-http 공식문서</a></li>
</ul>
<p>몇 가지 기능이 더 있는 useFetch 라이브러리가 따로 있다. (아샬님은 잘 안씀)</p>
<p>만약 조금 더 복잡해도 괜찮다면, 캐시 이슈를 고려한 좋은 대안이 있다. 👇</p>
<ul>
<li><a href="https://swr.vercel.app/ko">SWR</a></li>
<li><a href="https://tanstack.com/query/latest">TanStack Query(React Query)</a></li>
</ul>
<p>SWR 을 사용하면 컴포넌트는 주기적으로 데이터가 바뀌었는지 확인한다. 예를 들어, 게시판이면 게시물이 올라왔는지 확인한다.</p>
<p>React-Query 는 이보다 더 복잡한 것을 다룰 때 사용한다.</p>
<h1 id="useinterval-이란-강추">useInterval 이란? (강추)</h1>
<ul>
<li><a href="https://usehooks-ts.com/react-hook/use-interval">useInterval 공식문서</a></li>
</ul>
<p>setInterval 은 상태가 물려있으면 대부분 문제가 발생한다. setInterval 을 사용할 일이 있으면 무조건 useInterval 을 쓰자. </p>
<h1 id="setinterval-의-문제는">setInterval 의 문제는?</h1>
<ul>
<li><a href="https://overreacted.io/ko/a-complete-guide-to-useeffect/">useEffect 완벽 가이드 (필수)</a></li>
<li><a href="https://www.youtube.com/watch?v=2tUdyY5uBSw">React에서의 타이머 part 1 : setInterval 말고 이것! - 코드종님 영상 (필수)</a></li>
</ul>
<p>React 에서 setInterval 등을 쓸 때는 주의해야 부분이 있다. </p>
<p>아래 코드를 보자. </p>
<p>우리가 예상하는 결과는 <code>1초에 count 가 1씩 증가</code> 이다. 하지만 정상적으로 동작하지 않는다. 이유는 2가지이다. </p>
<p>첫 번째는 매 렌더링마다 <code>1초에 count 가 1씩 증가</code> 를 하는 새로운 타이머가 생긴다. 타이머가 여러 개 생기는 것이다. </p>
<p>두 번째는 타이머가 제대로 일을 하지 않는다. 첫 번째 타이머를 예로 들면 <code>1초에 count 가 1씩 증가</code> 가 아닌 <code>1초에 (count 값) 0 에 1이 증가</code> 를 수행한다. Closure 로 인해서 count 값이 0 으로 바인딩 되었기 때문이다. 그래서 결과를 보면 숫자가 오르락 내리락 한다. 모든 타이머가 사라지지 않고 남아있기 때문이다.</p>
<pre><code class="language-tsx">const [count, setCount] = useState(0);

setInterval(() =&gt; {
    setCount(count + 1);
}, 1000);

return &lt;h1&gt;{count}&lt;/h1&gt;;</code></pre>
<p>예를 들면 다음과 같다.</p>
<p>첫 번째 렌더링: count 값이 0으로 고정된다. 그래서 수행하는 0+1 이다. setState(1). 타이머는 남아있다.
→ 두 번째 렌더링: count 값이 1로 고정된다. 그래서 수행하는 일은 1+1 이다. setState(2). 타이머는 남아있다.
→ ...</p>
<h2 id="setinterval-을-해결하는-방법은">setInterval 을 해결하는 방법은?</h2>
<ul>
<li><a href="https://overreacted.io/making-setinterval-declarative-with-react-hooks/">Ref 활용</a></li>
</ul>
<p>그럼 어떻게 해결할 수 있을까?</p>
<p>리액트 컴포넌트에서 가장 중요한 점은 입력을 받아서 출력을 만드는 것이다. Props, Staet 모두 입력으로 볼 수 있다. 입력은 바깥의 일이다. 내부의 일이 아니다. 그래서 입력을 바꾸는 일도 Side Effect 이다.
리액트에서는 Side Effect 를 분리하는 일이 되게 중요하다. 왜냐하면 렌더링이 끊임없이 일어날 수 있는 구조이기 때문이다. 출력을 만드는 직접적인 일이 아니면 Side Effect 분리해야 한다.</p>
<p>그래서 위 코드를 useEffect 로 빼보자. 이렇게 했을 때 새로운 타이머를 만들면 그전 타이머는 치운다.</p>
<pre><code class="language-tsx">const [count, setCount] = useState(0);

useEffect(() =&gt; {
  const id = setInterval(() =&gt; {
      setCount(count + 1);
  }, 1000);
  return () =&gt; clearInterval(id);
})

return &lt;h1&gt;{count}&lt;/h1&gt;;</code></pre>
<p>하지만 이렇게 바꿔도 문제는 있다. 여전히 클로저를 고려해야 한다. </p>
<p>setInterval 이 세팅한 함수는 딱 한 번만 실행하고 변하지 않는데, 그 안에 setCount 인자의 count 값은 클로저에 인한 값이라 그 당시에 count 값을 갖는다. </p>
<p>그래서 이를 해결하려고 아래처럼 수정해보자.</p>
<pre><code class="language-tsx">const [count, setCount] = useState(0);

useEffect(() =&gt; {
  const id = setInterval(() =&gt; {
      setCount(value =&gt; value + 1);
  }, 1000);
  return () =&gt; clearInterval(id);
}, [])

return &lt;h1&gt;{count}&lt;/h1&gt;;</code></pre>
<p>하지만 이렇게 바꿔도 또 문제는 있다.</p>
<p>만약 count 가 state 가 아니라 prop 이라면 문제가 발생한다. 최신 prop 에 값을 사용하려면 setInterval 에 넘기는 함수가 매번 갱신돼야 한다.</p>
<p>결론은 이래저래 고려해야 할 사항이 많다. 그래서 useInterval 을 사용한다. 코드량도 줄고 클로저 등 신경 쓸 필요가 없다. 당시 렌더링 되는 그 시점의 상태에 대해서만 코드를 짤 수 있게 해준다.</p>
<pre><code class="language-tsx">const [count, setCount] = useState(0);

useInterval(() =&gt; {
  setCount(count + 1);
}, 1000)

return &lt;h1&gt;{count}&lt;/h1&gt;;</code></pre>
<p>useInterval 같은 게 절차적 프로그래밍이 아닌 선언적 프로그래밍의 특징을 갖는다고 한다. 이전에 무엇을 했는지 신경 쓸 필요가 없으며 다음에 무엇을 할지 신경 쓸 필요가 없다. 즉, 이전 렌더링과 다음 렌더링을 신경 쓸 필요가 없다. 현재 렌더링만 신경쓴다. </p>
<p>이것은 함수의 특징과도 같다. 내부적으로 선언한 변수는 다 초기화 해서 다시 실행시키기 때문에 이전에 어떤 값을 가졌는지 신경 쓰지 않는다. </p>
<p>선언적 프로그래밍은 그 시점에 필요한 입력만 주면 그 시점에 결과가 나온다. 절차적일 수 밖에 없었던 setInterval 을 useInterval 을 통해서 선언적으로 사용했다.</p>
<h1 id="useeventlistenr-란-추천">useEventListenr 란? (추천)</h1>
<ul>
<li><a href="https://usehooks-ts.com/react-hook/use-event-listener">useEventListenr 공식 문서</a></li>
</ul>
<p>모든 종류의 이벤트를 확인할 수 있다.</p>
<blockquote>
<p> 특히 dispatchEvent로 전달되는 커스텀 이벤트에 반응하기 좋다고 한다.</p>
</blockquote>
<h1 id="uselocalstorage-란-추천">useLocalStorage 란? (추천)</h1>
<ul>
<li><a href="https://usehooks-ts.com/react-hook/use-local-storage">useLocalStorage 공식 문서</a></li>
</ul>
<p>LocalStorage 에 객체를 넣는 경우가 많다. 그때 stringfy 와 parse 를 자동으로 해준다.</p>
<p>그리고 이보다 중요한 특징은 이벤트를 통해(dispatchEvent + useEventListener) 다른 컴포넌트와 동기화할 수 있다는 것이다.</p>
<p>예를 들어 다크 테마로 변경 됐을 때, 변경 사실을 어디로 알리지 않아도 된다. 알아서 처리한다.</p>
<h1 id="usedarkmode-란-추천">useDarkMode 란? (추천)</h1>
<ul>
<li><a href="https://usehooks-ts.com/react-hook/use-dark-mode">useDarkMode 공식 문서</a></li>
</ul>
<p>다크 테마 여부를 알 수 있다.</p>
<h1 id="아하-포인트">아하! 포인트</h1>
<p>useInterval 에 대해서는 <code>useEffect 완벽 가이드</code> 문서와 리액트 공식 문서를 읽어보면서 이해해야겠다.</p>
<p>useEventListener 을 쓰면 완벽히 분리할 수 있다고 하시는데 이 부분을 아직 이해 못하겠다.</p>
<p>useLocalStorage 도 쓰면서 이해해야겠다.</p>
<p>custom hook 을 만들 때 usehooks-ts 을 많이 참고해야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[회고를 왜 하시나요? 애자일 조직이 되고 싶나요?]]></title>
            <link>https://velog.io/@jeong_lululala/retrospective-Agile</link>
            <guid>https://velog.io/@jeong_lululala/retrospective-Agile</guid>
            <pubDate>Tue, 05 Sep 2023 06:01:16 GMT</pubDate>
            <description><![CDATA[<h1 id="회고를-왜-하시나요">회고를 왜 하시나요?</h1>
<p>여러분은 회고를 하고 계신가요? </p>
<p>며칠 전에 회고를 돕기 위한 서비스를 제작하는 팀을 만났습니다. 이 팀은 KTPA 라는 유명한 회고 양식을 토대로 많은 사람들이 회고를 하기를 바랬습니다.</p>
<p>하지만 정작 <code>왜</code> 회고를 해야 되는 지에는 대답을 잘 못하더군요. 회고는 일기가 아닙니다. 단순히 하루에 무엇을 했는지에 대해 쓰는 게 아니라는 말입니다.</p>
<p>회고를 하는 목적은 <strong>Future Action</strong> 을 도출해내기 위해서 입니다. Future Action  은 <strong>다음엔 어떤 시도를 해볼까?</strong> 입니다. 그래서 KTPA 는 Future Action 을 쉽게 도출하기 위해서 존재하는 도구라고 볼 수 있습니다.</p>
<p>그러니 회고를 통해 Future Action 을 도출하세요.</p>
<ul>
<li>내가 다음 주에 어떤 걸 실천하면 어떤 문제를 해결할 수 있을까?</li>
<li>이거 해보면 좋겠다?</li>
<li>당장 다음 주에 시도해볼 수 있는 게 있을까?</li>
</ul>
<p><strong>단, 지킬 수 없는 Action 은 도출하지 마세요!</strong> </p>
<p>Action 에 대한 FeedBack 까지 받아야 회고를 하는 의미가 있습니다. FeedBack 으로 어떤 문제가 어떻게 얼만큼 개선되었는지 확인하세요. </p>
<p>짧은 단위로, 예를 들면 일주일 단위로 Action 의 유효성에 대해 FeedBack 을 하세요.</p>
<p><strong>1. Action 도출하기</strong>
<strong>2. Action 을 실천하고 유효성에 대해 FeeBack 하기</strong></p>
<p>이 두 개가 전부이고 이게 에자일입니다. </p>
<p>마무리하겠습니다.</p>
<p>회고의 양식은 중요하지 않습니다. 기록하는 방법도 중요하지 않습니다. 하지만 Action 도출과 Action 의 실행이 어떤 효과가 있었는 지에 대한 FeedBack 은 반드시 있어야 합니다.</p>
<h1 id="애자일-조직이-되고-싶나요">애자일 조직이 되고 싶나요?</h1>
<p>에자일 조직이라고 말하면서 회고하는 방법에 집착하지 마세요.</p>
<p>진정한 애자일 조직은 다음과 같습니다.</p>
<ul>
<li>피드백이 되고 있는 조직이다.</li>
<li>되도록 자주 더 많이 빠르게 피드백을 하려고 한다.</li>
<li>피드백을 통해 더 자주 많이 빠르게 학습하려 한다.</li>
<li>피드백을 위해 어떤 시스템이 있고 어떤 노력을 하고 있다.</li>
</ul>
<p>피드백은 결국 학습입니다. 빨리 개발하는 조직이 애자일 조직이 아닙니다.
빨리 더 많이 학습하는 조직이 애자일 조직입니다. 후자는 고객한테 더 빨리 더 많은 가치를 줄 수 있습니다. 더 빨리 더 자주 우리 조직이 학습한 만큼 고객에게 줄 수 있는 가치가 늘어납니다.</p>
<h1 id="tdd-가-왜-애자일-practice-일까요">TDD 가 왜 애자일 Practice 일까요?</h1>
<p>TDD 가 왜 애자일 Practice 일까요? 피드백을 엄청 빨리 자주 하기 때문입니다. TDD는 코드를 치는 단계에서 피드백을 엄청 빨리 자주합니다. 이게 TDD의 목적이기 때문입니다.</p>
<p>그래서 TDD 가 단순히 테스트를 먼저 작성하는 것이라고 오해하시면 안됩니다. 테스트를 먼저 작성하는 게 에자일의 목적이 아니기 때문입니다. 우리는 테스트 코드 없이도 TDD 를 할 수 있습니다. 계속 짧은 주기로 동작하는지 확인하면 됩니다. 하지만 이게 귀찮으니 테스트 코드를 가지고 자동으로 확인하는 것입니다. 그래서 테스트 코드는 부산물일 뿐입니다.</p>
<h1 id="마지막으로">마지막으로</h1>
<p>여러분 에자일 책을 많이 읽는다고 에자일이 되지 않습니다. 자연스럽게 피드백을 많이 하려고 하세요. 그게 에자일입니다.</p>
<p>그리고 에자일은 혼자보다 함께하세요. 구성원의 합의하에 진행되는 에자일은 더 어렵지만 더 많은 것을 도출합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[useRef 에 대해 알아보자. 그리고 custom hook 에 대해 알아보자.]]></title>
            <link>https://velog.io/@jeong_lululala/useRef-custom-hook</link>
            <guid>https://velog.io/@jeong_lululala/useRef-custom-hook</guid>
            <pubDate>Tue, 05 Sep 2023 01:28:02 GMT</pubDate>
            <description><![CDATA[<h1 id="키워드">키워드</h1>
<ul>
<li>useRef</li>
<li>Hook의 규칙</li>
</ul>
<h1 id="최종-목표">최종 목표</h1>
<p>간단한 서버를 만들고, custom hook 을 사용하여 서버에서 데이터를 받아올 수 있다.</p>
<h1 id="현재-목표">현재 목표</h1>
<p>useRef 에 대해 알아보자. 그리고 custom hook 에 대해 알아보자.</p>
<h1 id="useref-란">useRef 란?</h1>
<p>컴포넌트의 생애주기 전체에 걸쳐서 유지되는 객체이다. 즉, 컴포넌트가 없어질 때까지 동일한 객체가 유지된다.</p>
<h1 id="useref-의-특징은">useRef 의 특징은?</h1>
<h2 id="동일한-useref-객체를-참조하면-이들은-동일한-객체를-공유한다">동일한 useRef 객체를 참조하면 이들은 동일한 객체를 공유한다.</h2>
<p>객체 자체는 값은 아니고, 값을 참조하기 위한 객체이다. 그래서 값을 언제든지 변경할 수 있다.
만약, 여러 컴포넌트에서 동일한 useRef 객체를 참조하면 이들은 동일한 객체를 공유하게 된다.</p>
<p>아래 <code>TimerControl</code> 컴포넌트가 10번 호출된다면 <code>ref.value</code> 는 10이 된다.</p>
<pre><code class="language-tsx">const ref = { value: 1 }

function TimerControl() {
    // useEffect 문제로 한 번만 늘어나진 않지만, 일단 한 번만 늘어난다고 가정한다.
    ref.value += 1; 

     return (...);
}</code></pre>
<h2 id="객체의-현재-값이-바뀌더라도-렌더링에-영향을-주지-않는다">객체의 현재 값이 바뀌더라도 렌더링에 영향을 주지 않는다.</h2>
<p>상태(state)가 변경되면 해당 컴포넌트와 하위 컴포넌트는 <strong>다시 렌더링</strong>한다. 하지만 레퍼런스 객체의 현재 값(current)는 바뀌더라도 <strong>렌더링에 영향을 주지 않는다.</strong> </p>
<p>다시 렌더링 되지 않기 때문에 UI 로는 바뀐 객체의 현재 값을 확인할 수 없다.</p>
<blockquote>
<p>컴포넌트의 생애주기는 컴포넌트가 생기고 없어질 때까지이다. 컴포넌트가 없어지면 가상 돔으로 들어갔던 컴포넌트 요소가 빠진다.</p>
</blockquote>
<h1 id="useref-는-언제-써야-할까">useRef 는 언제 써야 할까?</h1>
<h2 id="컴포넌트가-사라질-때까지-동일한-값을-쓰고-싶다">컴포넌트가 사라질 때까지 동일한 값을 쓰고 싶다</h2>
<p>input 태그의 id를 관리할 때 많이 쓴다.</p>
<p>input 태그를 가진 컴포넌트를 여러 곳에서 쓴다면, 각 컴포넌트 id는 달라야 하며 id가 생애주기 동안 유지가 되야 한다.</p>
<pre><code class="language-tsx">
function TimerControl() {
    const ref = useRef(`input-${Math.random()}`);

     return (
        &lt;div&gt;
            &lt;label htmlFor={id.current}&gt;
                    Search
            &lt;/label&gt;
            &lt;input
                 id={id.current}
                  ...
            /&gt;
          &lt;/div&gt;
    );
}</code></pre>
<h2 id="특히-useeffect-등과-함께-쓰면서-만나게-되는-비동기-상황에서-현재-값을-제대로-쓰고-싶다">(특히 useEffect 등과 함께 쓰면서 만나게 되는) 비동기 상황에서 현재 값을 제대로 쓰고 싶다.</h2>
<p>useEffect 등을 쓰면 Closure 문제를 만나게 된다. (Closure 문제는 우리가 useEffect 에서 변수가 Capture 되고 Bind 된다는 것에 대한 이해가 부족해서 일어나는 문제이다.)</p>
<p>Closure 문제에 대한 예를 들어보자. useEffect 가 실행되고 5초 뒤에 filterText 값을 console 에 출력하고 있다. 5초 이내에 filterText 의 값을 바꾸면 console 에는 바뀐 filterText 값이 찍힐까? 아니다. 처음에 Caputer, Bind 된 값인 &#39;&#39; 이 찍힌다.</p>
<pre><code class="language-tsx">const [filtetText, setFilterText] = useState(&#39;&#39;);

useEffect(() =&gt; {
    setTimeout(() =&gt; {
        console.log(filterText);
    }, 5_000);
}, []);

return &lt;input onChange={(e) =&gt; setFilterText(e.target.value)} /&gt;;</code></pre>
<p>바뀐 값을 출력하고 싶다면 다음과 같이 useRef 를 사용할 수 있다. query 가 <strong>값을 참조하는 객체</strong>라서 가능한 일이다.</p>
<pre><code class="language-tsx">const [filtetText, setFilterText] = useState(&quot;&quot;);
const query = useRef(&quot;&quot;); // 추가

// 추가
useEffect(() =&gt; {
  query.current = filtetText;
}, [filtetText]);

useEffect(() =&gt; {
  setTimeout(() =&gt; {
    console.log(query.current);
  }, 5_000);
}, []);

return &lt;input onChange={(e) =&gt; setFilterText(e.target.value)} /&gt;;</code></pre>
<blockquote>
<p>이렇게 코딩할 일은 절대 없긴 하다.</p>
</blockquote>
<h1 id="custom-hooks-란">Custom Hooks 란?</h1>
<p>로직을 재사용하기 위한 제일 쉬운 방법이다.</p>
<p>Custom Hooks 을 하는 방법은 너무 쉽다. Refactoring 으로 Extract Function 을 수행하면 된다. </p>
<p>컴포넌트가 PascalCase 로 이름을 붙였다면, Hook은 <code>use</code> 로 시작하는 camelCase로 이름을 붙이면 된다.</p>
<p><code>hooks/useFetchProducts.ts</code></p>
<pre><code class="language-tsx">export default function useFetchProducts() {
    const [products, setProducts] = useState&lt;Product[]&gt;([]);

    useEffect(() =&gt; {
        const fetchProducts = async () =&gt; {
            const url = &#39;http://localhost:3000/products&#39;;
            const response = await fetch(url);
            const data = await response.json();
            setProducts(data.products);
        };

        fetchProducts();
    }, []);

    return products;
}</code></pre>
<p>이제 <code>useFetchProducts</code> 를 여기저기서 불러다가 쓰면 된다.</p>
<p>setProducts 는 <code>useFetchProducts</code> 에서만 쓰는 함수이다. 그래서 이렇게 관심사의 분리로 컴포넌트를 떨구면 <code>setProducts</code> 가 캡슐화돼서 <code>setProducts</code> 를 실수로 잘못 쓰는 문제를 해소시킨다. 컴포넌트가 명확해진 것이다. 부르는 컴포넌트에서는 <code>setProducts</code> 가 쓰이든 말든 관심사 밖이기 때문에 상관이 없다.</p>
<blockquote>
<p>Custom Hook 을 잘 쓰게 되면 앞으로 테스트 코드를 작성할 때 Mocking 도 수월하게 할 수 있다. 그리고 Hook 자체를 유닛 테스트 할 수 있어서 편하다.</p>
</blockquote>
<h1 id="hook-규칙은">Hook 규칙은?</h1>
<p>Hook 호출은 규칙이 있어서 단순하게 쓰도록 노력해야 한다.</p>
<ol>
<li>Function Component 바로 안쪽(함수 최상위)에서만 호출한다.</li>
<li>Function Component 또는 Custom Hook 에서만 호출한다.</li>
</ol>
<p>⚠️ 다음과 같이 콜백 함수나 조건문 안에서 Hook 을 호출하면 안된다. ⚠️</p>
<pre><code class="language-tsx">    if(playing) {
        const products = useFetchProducts();
          console.log(products);
    }</code></pre>
<blockquote>
<p>이런 형태로 특정 조건이 걸렸을 때 다시 부르고 싶다면, fetchProducts() 같은 re-load 하는 함수를 products 처럼 Return 해준다. 그리고 특정 조건에 fetchProducts() 를 호출한다.</p>
</blockquote>
<h1 id="아하-포인트">아하! 포인트</h1>
<p>컴포넌트의 깔끔함을 위해서 custom hook 을 쓰는 연습을 많이 해야겠다. useEffect 는 외부와 연결을 위해서 쓰는 hook 이니까 무조건 custom hook 으로 빼도 될 듯 싶다.</p>
<h1 id="다음에는">다음에는?</h1>
<p>usehook-ts 라이브러리에 대해 알아보자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[경기청년 갭이어 4주차 회고, 이젠 정말 팀 프로젝트가 되었다]]></title>
            <link>https://velog.io/@jeong_lululala/gap-year-4-weeks</link>
            <guid>https://velog.io/@jeong_lululala/gap-year-4-weeks</guid>
            <pubDate>Mon, 04 Sep 2023 05:20:55 GMT</pubDate>
            <description><![CDATA[<h1 id="facts--feelings--findings">Facts &amp; Feelings &amp; Findings</h1>
<p><strong>캡스톤 대면 회의</strong></p>
<p>월요일과 일요일에 캡스톤 회의를 했다. </p>
<p>월요일에는 디자이너를 선택하고 우리 프로젝트를 구체화시켰다.
일요일에는 디자이너님께 드릴 자료를 정리하고, 총학생회 미팅도 준비했다.</p>
<p>매번 회의를 준비해 가고 있다. 준님의 스터디 방식과 유사하게 구글 스프레드 시트에 준비해 간다. 스프레드 시트는 수동적인 사람도 능동적으로 만드는 힘이 있다. 그리고 회의에 집중시키는 힘이 있다. 그래서 비대면 회의를 하게 되더라도 소통이 잘 돼서 좋다.</p>
<p>다만, 하나의 셀에 데이터가 많아지면 보기 불편한 점이 있다. 이 부분은 노션이나 피그마로 빼야겠다.</p>
<p><strong>Playwright 에 대한 공부</strong></p>
<p>개발 준비를 할 수 있는 기간이 얼마 남지 않았다. 생존코스에서 부족했던 부분을 다시 보고 있다.
이번에 MSW 와 E2E 테스트에 대해 더 이해하게 되었다. jest.mock 이 많아 지면 보기 불편하니까, MSW 로 따로 빼서 Mocking 한다는 점을 알아냈다. 그리고 실제 테스트를 위해 E2E 테스트를 한다는 점도 알아냈다.
추가로 CI/CD 에 대해서도 공부하게 됐는데 팀 프로젝트를 할 때 일관성 있는 코드를 위해서 Github Actions를 이용해서 CI/CD 도입해야겠다고 생각했다.</p>
<p><strong>두 번째 디자이너 미팅</strong></p>
<p>이번이 디자이너님을 두 번째로 뵙는 날이었다. 간단한 와이어 프레임과 기능 목록을 준비해갔다. 준비 과정에서 궁금했던 질문들을 여쭤보면서 머리 속으로 구체화를 시켰다. 예를 들어, 네비게이션바에는 몇 개의 요소가 적당한지, 메인 페이지는 어떤 게 오면 좋을지 등에 대해 여쭤봤다. 회의가 끝나고 디자이너님은 더 자세한 와이어 프레임과 구체적인 기능 목록을 요청하셨다. 그래서 우리는 회의가 끝나고 이틀 동안 메인 페이지 시안을 그리고 축제에 대한 정보 수집을 했다. 다들 시간도 많이 쓰고 고생했을 것이다. 그래도 잘 마무리해서 월요일 날 보내드리게 되었다.</p>
<p><strong>메가테라 백엔드 접수</strong></p>
<p>메가테라 백엔드 코스를 신청하기 위해 상담을 받았다. 가지고 있던 고민을 모두 털어놓는 시간이었다. 노아님을 5개월만에 다시 뵀다. 나를 못 알아보시는 듯 했다. 수염이 많아지셨는데 요즘 되게 바쁘시다고 한다. &quot;생존코스에서 배운 것을 어떻게 팀 프로젝트에 녹여야 할 지 모르겠어요&quot; &quot;팀원들이 역량이 되지 않는다면 도입하지 않는게 맞습니다. 팀원들은 아무리 좋은 기술이라도 자신들이 잘 못하면 계속 부정적인 부분만 찾을 거예요. 가장 역량이 낮은 팀원에 맞춰서 프로젝트를 진행하는 방법밖에 없습니다. 회사도 이런 식으로 돌아가요.&quot; 대답을 듣고 고민이 많아졌다. 대학생의 역량 한없이 낮아질 수 있다. 실제로 출시될 수 없는 서비스가 될 수도 있다. 멱살잡고 어떻게서든 끌고 갈 수는 없을까. 이 부분에 대해서는 팀원들과 회의를 더 해봐야겠다.</p>
<p>오늘 상담을 통해서도 느꼈지만, 노아님은 참 따뜻한 사람이다. 나의 고민에 진지하고 진심으로 답해 주신다. 생각해보면 윤석님, 준님, 홀맨님 등 멋있다고 생각이 드는 개발자들은 다 공감능력이 좋았다. 경험이 많아서 그런 것 같다. 다 겪어본 일이니 진심으로 공감해주고 해결책을 주시는 것 같다. 나도 이런 사람이 되고 싶다.</p>
<p>다음은 노아님이 해주신 말씀을 정리해봤다. 이 프로젝트를 팀으로 하게 된다면, 나는 리드 개발자가 될 것이다. 백엔드에 대한 지식이 전체 그림을 짜는데 짜는데 도움이 될 거라고 하셨다.리드 개발자로서 API 설계, 프로젝트 매니징, 시작 부분을 어떻게 끊을 것인지에 대해 그림을 그려야겠다.노아님은 완벽하게 보다는 꾸준히, 반복학습을 권장하셨다. 메가테라에서 알게 된 모든 코드는 내것이 아니기에 착각하지 말고 몇 번이고 반복해서 내것으로 만들어야 한다. 시간이 되면 데이터베이스 공부도 필수이다. 그 후에는 flutter도 해보는 것을 추천. 대기업에서 외주는 경력으로 안 쳐준다. 네이티브는 커리어 상으로 별로이다. 숙련도가 쌓였다 싶으면 깊게 공부해야한다. 웹/앱으로 할 경우 디자인은 크게 3개를 그린다. 데스크탑, 모바일, 태블릿. 백엔드까지 공부하고 혼자서 플젝 2개까지 해보기. 그러면서 웹에 대한 느낌을 찾아야 한다. 도움이 필요하면 개인 DM 요청하라고 하셨다.
백엔드 코스를 수강하기 전에 자바에 대한 기본 개념을 전체 훑으라고 하셨다.</p>
<h1 id="future-action-plan">Future Action Plan</h1>
<p>프로젝트 과정 남기기 문서화</p>
<ul>
<li>프로젝트 회고</li>
<li>기록을 많이 해라, 작업 일지 (데브노트), </li>
<li>작업을 왜하고 어떻게 할지 </li>
<li>작업일지, 공부하면서 찾은 키워드, 노트정리, → 회고(프로젝트 회고, 팀원들과 회고), 주기적인 팀원들과 회고 (회고⇒이해못하고 프로젝트하는 것을 발견)</li>
</ul>
<p>하나의 기능을 만들어도 왜? 했는지 작성</p>
<ul>
<li>플로젝트 하면서 쌓아놓아야할 것-</li>
<li>같이 하면서 주의해야 할 것</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Hooks 에 대해 알아보자]]></title>
            <link>https://velog.io/@jeong_lululala/react-hook</link>
            <guid>https://velog.io/@jeong_lululala/react-hook</guid>
            <pubDate>Sat, 02 Sep 2023 04:39:57 GMT</pubDate>
            <description><![CDATA[<h1 id="키워드">키워드</h1>
<ul>
<li>React Hook 이란</li>
<li>Hooks<ul>
<li>useState</li>
<li>useEffect</li>
<li>useContext</li>
<li>useRef</li>
<li>useLayoutEffect</li>
</ul>
</li>
<li>React StrictMode 란</li>
</ul>
<h1 id="최종-목표">최종 목표</h1>
<p>간단한 서버를 만들고, custom hook 을 사용하여 서버에서 데이터를 받아올 수 있다.</p>
<h1 id="현재-목표">현재 목표</h1>
<p>React Hooks 에 대해 알아보자.</p>
<h1 id="hooks-의-등장-배경은-기존-방식의-문제점">Hooks 의 등장 배경은? 기존 방식의 문제점</h1>
<p>React Hooks 는 React 16.8 버전에서 처음 도입되었다. 기본 방식에 있던 몇 가지 문제를 해결했다.</p>
<p>기본 방식의 문제점은 다음과 같다.</p>
<blockquote>
<p><strong>1. Wrapper Hell (HoC): 고차 컴포넌트</strong></p>
<p>컴포넌트 로직을 재사용하기 위해서 컴포넌트를 다른 컴포넌트로 감싸고 Props 로 내려준다. 이 고차 컴포넌트 방식은 코드를 복잡하게 만들고, 컴포넌트 계층 구조를 복잡하게 만든다.</p>
<pre><code class="language-js">const EnhancedComponent = higherOrderComponent(WrappedComponent);</code></pre>
</blockquote>
<blockquote>
<p><strong>2. Huge Components</strong></p>
<p>로직이 컴포넌트 안에 들어가면서 컴포넌트가 거대해지는 문제가 있다. 이로 인해 유지보수가 어렵다.</p>
</blockquote>
<blockquote>
<p><strong>3. Confusing Classes</strong></p>
<p>클래스형 컴포넌트를 사용할 때, this 키워드가 어떤 것을 가리키는지 혼란스러울 수 있다.</p>
</blockquote>
<p>React Hooks 는 React 를 쓰는 방식을 완전히 바꾼 커다란 변화이다. 함수형 컴포넌트에서 hooks 로 모든 게 처리가 가능하다. </p>
<p>기존에 있던 클래스형 컴포넌트를 없애건 아니지만 이제는 기존으로 돌아가는 게 불가능하다.</p>
<h1 id="hooks-도입-전후-비교하기">Hooks 도입 전후 비교하기</h1>
<p><strong>기존</strong></p>
<ul>
<li>상태를 가진 컴포넌트는 <code>Class Component</code> 로 만든다.</li>
<li>Props 만 사용하는 재사용이 용이한 작은 컴포넌트는 <code>Function Component</code> 로 작성한다. </li>
<li>Redux 에서도 이런 비슷한 구분이 존재했었다.</li>
</ul>
<p><strong>현재</strong></p>
<ul>
<li>그냥 <code>Function Component</code> 만 사용한다.</li>
<li>상태 관리 유무를 바로 알기 어렵다. === 신경쓰지 않아도 되는 부분이다.<blockquote>
<p>기존에는 Class Component 면 상태를 관리하는 컴포넌트라고 예상할 수 있었다. 지금은 State Hook (useState) 을 이용해서 처리하니까 알 수도 없고 신경 쓸 필요도 없게 됐다. 패러다임이 바뀐 것이다.</p>
<p>대신 알 수 없게 되면서 조금 어려워진 점이 있다. Props 가 완전히 같으면 컴포넌트를 실행하지 않게 하고 싶을 때가 있다. 이때 useMemo 같은 hook 으로 처리해야 한다. 근데 내부에서 뭔가를 처리해야 하는 게 있으면 문제가 생길 가능성이 있다. (그래서 섣부른 최적하는 하지 말라는 얘기가 나온다.)</p>
</blockquote>
</li>
</ul>
<ul>
<li><p>복잡한 요소는 전부 Hook 으로 격리 및 재사용이 가능하다.</p>
<blockquote>
<p>기존에는 컴포넌트 안에 많은 것이 들어있어서 컴포넌트가 거대해지는 문제점이 있었다. HoC 같은 걸로 빼지 않으면 굉장히 복잡해졌는데, Hooks 로 빼는 건 너무 쉽다.</p>
</blockquote>
</li>
</ul>
<h1 id="대표적인-hooks-는">대표적인 Hooks 는?</h1>
<ul>
<li>useState [State Hook]</li>
<li>useEffect [Side-Effect] </li>
<li>useContext</li>
<li>useRef</li>
<li>useLayoutEffet</li>
</ul>
<h1 id="useeffect-란">useEffect 란?</h1>
<ul>
<li><p>렌더링 이후에 해야 할 일, 즉 <strong>React의 외부와 관련된 일</strong> 을 정해줄 수 있다.</p>
<blockquote>
<p>그래서 공식문서에서는 &#39;정말로 외부랑 동기화 하는 게 아니면 사용하지 말아라&#39; 라고 나온다.
<code>useEffect</code> 는 React 의 외부와 동기화 하는 일(Side-Effect)을 처리해야 한다. </p>
</blockquote>
</li>
<li><p>기본적으로 렌더링 때마다 실행되므로, 의존성 배열을 통해서 언제 이펙트를 실행할 지 지정할 수 있다.
(= 불필요한 경우에 건너뛸 수 있다.)</p>
</li>
<li><p>함수를 리턴함으로써 종료 처리를 할 수 있다.</p>
</li>
</ul>
<h1 id="useeffect-를-이용한-타이머-예제---외부-동기화">useEffect 를 이용한 타이머 예제 - 외부 동기화</h1>
<p>다음은 외부 동기화[synchronization] 에 대한 것이다. 이 정도는 useEffect 를 안 쓴다고 크게 문제가 되지 않지만, useEffect를 같이 쓰는 습관을 들이자.</p>
<pre><code class="language-tsx">// No
document.title = `Now: ${new Date().getTime()}`;</code></pre>
<p>이렇게 useEffect 를 사용해서 처리하는 게 훨씬 우아한 방법이다.</p>
<pre><code class="language-tsx">// Yes
useEffect(() =&gt; {
    document.title = `Now: ${new Date().getTime()}`;
});</code></pre>
<h1 id="useeffect-를-이용한-타이머-예제---문제가-있는-코드">useEffect 를 이용한 타이머 예제 - 문제가 있는 코드</h1>
<p>우리가 원하는 것은 다음과 같다.</p>
<ul>
<li>계속 가고 있는 타이머의 현재 시간이 title 에 보인다.<ul>
<li>버튼을 눌러서 OFF 상태 → 타이머의 시간이 멈춘다. title 에 보이는 타이머의 시간도 멈춘다.</li>
<li>버튼을 눌러서 ON 상태 → 타이머의 현재 시간이 다시 간다. title 에 보이는 타이머의 시간도 다시 간다.</li>
</ul>
</li>
</ul>
<p>하지만 OFF 상태일 때 시간이 멈추지 않는 문제가 발생한다.</p>
<pre><code class="language-tsx">function Timer() {
    useEffect(() =&gt; {
        setInterval(() =&gt; {
            document.title = `Now: ${new Date().getTime()}`;
        }, 100);
    });

      return (
        &lt;p&gt;Playing&lt;/p&gt;
    );
}

export default function TimerControl() {
    const [playing, setPlaying] = useState(false);

      const handleClick = () =&gt; {
        setPlaying(!playing);
    };

      return (
        &lt;div&gt;
            {playing ? (
                  &lt;Timer /&gt;
            ) : (
                &lt;p&gt;Stop&lt;/p&gt;
            )}
            &lt;button type=&quot;button&quot; onClick={handleClick}&gt;
                Toggle
            &lt;/button&gt;
        &lt;/div&gt;
    );
}</code></pre>
<h1 id="useeffect-를-이용한-타이머-예제---함수-종료-처리">useEffect 를 이용한 타이머 예제 - 함수 종료 처리</h1>
<p>버튼 OFF 를 누르면 Timer 컴포넌트 자체가 없어진다. 이때 setInterval 도 같이 없애야 한다.</p>
<p>이를 위해서 useEffect에 함수 종료 처리(Clean Up)를 넣어야 한다.</p>
<pre><code class="language-tsx">function Timer() {
    useEffect(() =&gt; {
        const id = setInterval(() =&gt; {
            document.title = `Now: ${new Date().getTime()}`;
        }, 100);

        // Clean Up
          return () =&gt; {
          console.log(&#39;End of Effect&#39;);
          cleanInterval(id);
        };
    });

      return (
        &lt;p&gt;Playing&lt;/p&gt;
    );
}

export default function TimerControl() {
    const [playing, setPlaying] = useState(false);

      const handleClick = () =&gt; {
        setPlaying(!playing);
    };

      return (
        &lt;div&gt;
            {playing ? (
                  &lt;Timer /&gt;
            ) : (
                &lt;p&gt;Stop&lt;/p&gt;
            )}
            &lt;button type=&quot;button&quot; onClick={handleClick}&gt;
                Toggle
            &lt;/button&gt;
        &lt;/div&gt;
    );
}</code></pre>
<h1 id="useeffect-의존성-배열이란">useEffect 의존성 배열이란?</h1>
<p><strong>처음에 한번만 실행하기</strong></p>
<p>의존성 배열에 아무것도 지정하지 않으면 맨 처음에 딱 한번만 실행된다. 주로 API를 호출해서 데이터를 얻을 때 사용한다.</p>
<pre><code class="language-tsx">const [products, setProducts] = useState&lt;Product[]&gt;([]);

useEffect(() =&gt; {
    const fetchProducts = async () =&gt; {
        const url = &#39;http://localhost:3000/products&#39;;
        const response = await fetch(url);
        const data = await response.json();
        setProducts(data.products);
    };

    fetchProducts();
}, []);</code></pre>
<p>위에서 작성한 함수를 Effect 밖으로 빼는 것으로 수정해도 정상적으로 작동한다. 
(하지만 다른 예시에서는 오류가 날 수 있다. 아래 &#39;함수를 Effect 안으로 옮겨야 하는 이유&#39;를 보자.)</p>
<pre><code class="language-tsx">const [products, setProducts] = useState&lt;Product[]&gt;([]);

const fetchProducts = async () =&gt; {
  const url = &#39;http://localhost:3000/products&#39;;
  const response = await fetch(url);
  const data = await response.json();
  setProducts(data.products);
};

useEffect(() =&gt; {
    fetchProducts();
}, []);</code></pre>
<h1 id="함수를-effect-안으로-옮겨야-하는-이유는">함수를 Effect 안으로 옮겨야 하는 이유는?</h1>
<p><a href="https://overreacted.io/ko/a-complete-guide-to-useeffect/#%ED%95%A8%EC%88%98%EB%A5%BC-%EC%9D%B4%ED%8E%99%ED%8A%B8-%EC%95%88%EC%9C%BC%EB%A1%9C-%EC%98%AE%EA%B8%B0%EA%B8%B0">useEffect 완벽가이드 - 함수를 Effect 안으로 옮겨야 하는 이유</a></p>
<p>다음과 같은 예시가 있다. useEffect 는 클로저라서, 맨 처음에 잡힐 때 바깥에 있던 변수들을 캡쳐한 다음에 그걸 바인딩해서 사용한다.</p>
<p>그러면 변수의 값이 바뀌어도 동기화하는데 실패할 수 있다.</p>
<pre><code class="language-tsx">function SearchResults() {
  const [query, setQuery] = useState(&#39;react&#39;);

  function getFetchUrl() {
    // ...
    return &#39;https://hn.algolia.com/api/v1/search?query=&#39; + query;
  }

  async function fetchData() {
    const result = await axios(getFetchUrl());
    setData(result.data);
    // ...
  }

  useEffect(() =&gt; {
    fetchData();
  }, []);

  // ...
}</code></pre>
<p>이런 문제를 해결할 방법은 &#39;함수를 Effect 안으로&#39; 옮기면 된다.</p>
<pre><code class="language-tsx">function SearchResults() {
  const [query, setQuery] = useState(&#39;react&#39;);

  useEffect(() =&gt; {
    function getFetchUrl() {
      return &#39;https://hn.algolia.com/api/v1/search?query=&#39; + query;
    }

    async function fetchData() {
      const result = await axios(getFetchUrl());
      setData(result.data);
    }

    fetchData();
  }, [query]);
  // ...
}</code></pre>
<h1 id="effect-가-두-번-실행되는-문제는">Effect 가 두 번 실행되는 문제는?</h1>
<p><code>&lt;React.StrictMode&gt;</code> 로 컴포넌트 전체를 감쌀 경우, 예상치 못한 Side Effect 를 찾으려고 Effect 등을 두 번씩 실행한다. </p>
<p>평소에는 큰 문제가 없지만, API 등을 사용하면 이상하다고 느낄 수 있으니 참고하자.</p>
<blockquote>
<p>단, Strict 모드는 개발 모드에서만 활성화되기 때문에, 프로덕션 빌드에는 영향을 끼치지 않는다.</p>
</blockquote>
<h1 id="의존성-배열을-이용해-fetch-할-때-주의사항은">의존성 배열을 이용해 Fetch 할 때 주의사항은?</h1>
<p>다음 예시를 보자. 의존성 배열에 있는 <code>userId</code> 의 값이 바뀌면 useEffect 가 실행된다.</p>
<p>이전에 불러오던 것을 멈출 수 없다. 그래서 아까 값을 불러오던 것과 지금 불러오는 것, 둘 다 업데이트를 하게 된다.</p>
<p>누가 먼저 응답이 올 지 모른다. 먼저 실행했다고 먼저 응답이 오는 것은 아니다.</p>
<pre><code class="language-jsx">useEffect(() =&gt; {

  async function startFetching() {
    const json = await fetchTodos(userId);
    if (!ignore) {
      setTodos(json);
    }
  }

  startFetching();

}, [userId])</code></pre>
<p>그래서 다음과 같이 <code>ignore</code> 변수를 이용해 종료 처리를 해야 한다. 그러면 <code>userId</code> 가 바뀔 때 이전의 <code>ignore</code>는 <code>true</code> 가 돼서 <code>setTodos(json)</code> 이 무시된다. </p>
<pre><code class="language-jsx">useEffect(() =&gt; {
  let ignore = false;

  async function startFetching() {
    const json = await fetchTodos(userId);
    if (!ignore) {
      setTodos(json);
    }
  }

  startFetching();

  return () =&gt; {
    ignore = true;
  };
}, [userId])</code></pre>
<h1 id="참고">참고</h1>
<ul>
<li><a href="https://ko.legacy.reactjs.org/docs/higher-order-components.html">고차 컴포넌트 - React 공식 문서</a></li>
<li><a href="https://react.dev/learn/synchronizing-with-effects">useEffect 공식문서</a></li>
<li><a href="https://react.dev/learn/you-might-not-need-an-effect">useEffect 는 React의 외부와 동기화 할 때만 사용해라 - 공식문서</a></li>
<li><a href="https://overreacted.io/ko/a-complete-guide-to-useeffect/">useEffect 완벽 가이드 (필독)</a></li>
</ul>
<h1 id="다음에는">다음에는?</h1>
<p>fetch 처리하는 부분을 다른 컴포넌트로 옮기려고 하면, fetch 하는 부분 뿐만 아니라 import 하는 부분도 옮겨야 한다. 그래서 복붙 과정에서 실수할 수도 있다.</p>
<p>얘네를 단순한게 옮길 수 있는 방법이 있는데 다음에 알아보자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[경기청년 갭이어 3주차 회고, 인생은 불확실성의 연속이구나]]></title>
            <link>https://velog.io/@jeong_lululala/gap-year-3-weeks</link>
            <guid>https://velog.io/@jeong_lululala/gap-year-3-weeks</guid>
            <pubDate>Tue, 29 Aug 2023 07:27:58 GMT</pubDate>
            <description><![CDATA[<h1 id="facts-한-것">Facts (한 것)</h1>
<p>6차 교육을 끝으로 역량 핵심 교육을 마쳤다.</p>
<p>5차 교육 때는 태릉좀비촌의 임태운 작가님이 멘토로서 교육을 하셨고,
6자 교육 때는 예산 신청서를 작성하고 제출하는 시간을 가졌다.</p>
<h1 id="feelings-느낀-것">Feelings (느낀 것)</h1>
<p>갭이어는 이런 인상을 남겼다. &#39;모든 사람에게 배울 점이 있다.&#39;</p>
<p>역량 핵심 교육 기간 동안 마이크를 한 사람도 빠짐없이 잡아보며 모두가 주인공이 되는 경험을 했다. 나는
마이크를 잡은 모든 사람에게 배울 점이 있었다. 태도, 발상, 생각, 열정 등. 평소에는 느끼지 못했던 살아있음을 느꼈다. 그들의 이야기를 들으며 나는 더 나은 사람이 되고 싶어졌다. </p>
<p>우리는 수많은 시간을 다르게 살아왔기 때문에 서로에게 배움을 줄 수 밖에 없는 존재였던 것이다.</p>
<hr>
<p>궁금증이 있었다. 요즘 많은 사람들이 문단을 왜 가볍게 나누는지 궁금했다. 예전에는 한 문단 안에 여러 문장이 있었고 핵심 문장이 있었다. 지금은 좀 다르다. 한 문장도 문단이 될 수 있다. 나도 블로그에 글을 쓸 때 언제부턴가 그렇게 쓰기 시작했다.</p>
<p>이 부분에 대해서 작가님이 명쾌한 답을 주셨다. 디지털 기기로 정보를 보는 시대가 왔다. 스마트폰은 이전의 책과 신문에 비하면 화면이 훨씬 작다. 그래서 문단에 정보가 많으면 보기가 힘들다. 읽기가 싫어진다. 더구나 요즘 사람들은 책을 잘 안 읽어서 긴 글을 읽지 못한다. 그래서 문단을 가볍게 나누게 되는 것이다. 생각해보면 웹 소설도 문단이 가볍게 나뉘어 있다.</p>
<hr>
<p>8월은 불확실성의 연속임이었다. 착착착 해결되는 일이 없었다.</p>
<p>서비스가 출시되려면 총학생회의 협력이 무엇보다 중요하다. 총학생회가 축제에 대한 정보를 넘겨주지 않으면 아무리 잘 만들어도 서비스화가 되기 어렵다. 근데 방학이라 그런지 연락이 안됐다. 학생지원과에도 연락을 해보고 인스타그램 DM으로도 연락을 해봤지만, 접촉이 어려웠다. 약 2주 동안 끈질기게 DM을 보내자 개강 직전인 지금에 답장이 왔다. 답장이 온 게 다행이다. 9월 초에는 무조건 미팅을 해야 겠다.</p>
<p>디자이너를 구하면서도 어려움을 겪었다. 교내 디자이너 중에서 하고 싶은 사람과 하면 된다고 생각했다. 하지만  실력이 꽤나 있으며 갭이어의 조건을 만족하며 우리와 잘 맞는 사람을 구하긴 쉽지 않았다. 그리고 지금은 1명을 구했으나 이 사람이 언제 도망칠지 모른다는 불안감에 쌓여있다.</p>
<p>설문조사를 배포하면서도 어려움을 겪었다. 흥미로운 서비스라고 생각돼서 설문조사에 많이 참가할 줄 알았는데 생각보다 많이 참여하지 않았다. 60명 정도 참여했다. 평소에 &#39;우리집 고양이 좀 보고 가&#39; 이런 글에는 좋아요가 몇 백 개 씩 달리는 커뮤니티인데, 유도가 어려웠다.</p>
<p>단기간에 깔끔하게 일이 끝나는 건 엄청난 욕심이었다. 사람과 사람의 일은 쉽게 끝나는 일이 없다.</p>
<hr>
<p>나의 고민은 늘 가치가 있구나. 회고를 쓰며 다시 한번 느낀다.</p>
<h1 id="findings-배운-것">Findings (배운 것)</h1>
<p>작가님이 말씀하시길, 하루 집중 할 수 있는 시간은 3시간이다. 영화의 러닝타임이 최대 3시간인 이유이다. 이에 공감하는게 하루 종일 개인 공부를 할 때와 학교를 다니며 개인 공부를 할 때, 개인 공부의 양이 똑같다. 집중한 3시간이 넘어가면 나머지 시간은 흐지부지 보내기 때문이다.</p>
<hr>
<p>끝나지 않는 일에 메달리지 말자. 비동기적으로 움직이자. 세상은 불확실성으로 가득 찼다. 메달린다고 해결되지 않으며 시간이 필요한 일 많다.</p>
<h1 id="future-할-것">Future (할 것)</h1>
<p>하루 집중 시간, 3시간을 알차게 보낼 방법을 찾아보자. 내가 가장 집중을 잘하는 환경, 시간대를 찾아보자.</p>
<hr>
<p>해야할 일을 정리하고 하나씩 해결하자. 그리고 주간회고는 매주 잊지 말자.</p>
]]></description>
        </item>
    </channel>
</rss>