<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>popolarburr.log</title>
        <link>https://velog.io/</link>
        <description>차곡차곡</description>
        <lastBuildDate>Fri, 20 Sep 2024 08:18:15 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. popolarburr.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/bxxloob_-" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[회고] 펄어비스 QA 비기너 인턴십 최종 합격 후기]]></title>
            <link>https://velog.io/@bxxloob_-/%ED%9A%8C%EA%B3%A0-%ED%8E%84%EC%96%B4%EB%B9%84%EC%8A%A4-QA-%EB%B9%84%EA%B8%B0%EB%84%88-%EC%9D%B8%ED%84%B4%EC%8B%AD-%EC%B5%9C%EC%A2%85-%ED%95%A9%EA%B2%A9-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@bxxloob_-/%ED%9A%8C%EA%B3%A0-%ED%8E%84%EC%96%B4%EB%B9%84%EC%8A%A4-QA-%EB%B9%84%EA%B8%B0%EB%84%88-%EC%9D%B8%ED%84%B4%EC%8B%AD-%EC%B5%9C%EC%A2%85-%ED%95%A9%EA%B2%A9-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Fri, 20 Sep 2024 08:18:15 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/7ae845e5-6b59-43e4-844e-36e9602c45a5/image.png" alt=""></p>
<p>** 채용 프로세스에 대한 정보 및 팁은 없습니다.**</p>
<br>

<br>

<br>




<p>약 1달간의 채용 프로세스가 끝났다. </p>
<p>처음 자소서를 작성하기 시작하고, 매일 매일 퇴고하면서 수정하고 확인하며 맘을 졸였다.</p>
<p>그러나 자소서를 작성하는 단계부터 재밌었던건 <strong>게임이력</strong> 을 작성하게 했기 때문이다.</p>
<p>나는 운동도 좋아하고 음악도 좋아하지만 게임도 엄청 좋아한다. 그렇기에, 어릴 때부터 해오던 게임들을 자소서 포맷에 상관없이 쭈욱~ 나열하다보니 4천자 가까이 작성했고, 작성하면서 자연스럽게 이 회사에 대해서 더 큰 관심을 갖게 되었고 재미도 붙였다. </p>
<p>그렇게 자소서 제출 후 얼마 시간이 지나지 않아 합격 통보 후 이틀 내에 인적성 검사를 보라고 날라왔다. 당시에 ISTQB 시험 전날이였기에, 늦은 시각(새벽)이 되어서야 시작했고, 대략 3시간? 정도 본 것 같다. </p>
<p>그렇게 보고 나서 피곤함을 이끌고 ISTQB시험을 봤고, 다행히 합격했다. </p>
<p>그러면 나에게 주어진건 인적성 결과였다. 나름 결과 발표 텀이 길었기에 다른 회사도 지원해보면서 기다렸다. 그 후에 날라온 합격 메일 및 면접 일정. </p>
<br>

<p>그렇게 면접을 엄~청 열심히 준비하고 펄어비스 및 검은사막에 관련된 정보를 정말 박박 긁어모으면서 준비 또 준비했다 .</p>
<p>면접 시작 10분후까지 긴장을 너무해서 자기소개를 여러번 할정도로 긴장했다. </p>
<p>다행히도 면접대기실에서 만난 다른 지원자와 이야기를하며 가볍게 긴장을 풀었지만, 쉽사리 긴장이 풀리지 않았다. </p>
<p>면접에서는 너무 긴장도 많이하고 할 말도 많아 내용이 산으로 가기도 했고, 말을 중간에 끊어버리는 불참사까지 벌어지면서 망했다고 생각했다.(회고를 적지 않은 이유)</p>
<br>


<p>면접이 2주 지난 오늘(20일), 결과 발표에 합격을 받았다. </p>
<blockquote>
<p>처음엔 진짜 띠-용 ? 그 자체였다. 왜지? 나 면접 정말 못본거 같은데 ? </p>
</blockquote>
<p>그래도 합격은 합격이니 기분이 너무 좋다 . </p>
<p>QA로서 첫 면접경험, 겜돌이인 나에게 게임 회사의 합격, 집에서 10분거리인 회사, 미1친 회사의 복지, 여러모로 정말 맘에 들었다 . </p>
<p>마냥 좋아하고 있을 떄 합격을 할 수 있었던 요인과 계기가 뭘까 고민해봤는데, 딱 두가지 인 것 같다.</p>
<blockquote>
<p><strong>게임에 대한 진심</strong> 과 <strong>QA에 대한 열정</strong></p>
</blockquote>
<p>이를 자소서에 녹이기도 했고 면접때도 가감없이 다 보여줬던거 같다.</p>
<p>이를 경험삼아 더 성장하고 열심히해야겠다는 생각이 든다 .</p>
<p>!펄어비스 짱 !</p>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/3697aeae-6e1b-43ef-8708-bac6a952e586/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/0f58ee65-fc6d-42dd-b47b-8ff18136d45d/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/e4d7f4a2-3546-430a-ae43-07b1c5808cfd/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/82ec4421-c74c-4890-a182-92a0abc80afc/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/bd484292-c9a4-4b31-84f8-d56e6acc4039/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/91478d70-b7d6-4da1-86de-dbb28da300f5/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[회고] 엔테크서비스 SW QA 엔제니어 공채 1차 면접 회고 ]]></title>
            <link>https://velog.io/@bxxloob_-/%EB%A9%B4%EC%A0%91%ED%9B%84%EA%B8%B0-%EC%97%94%ED%85%8C%ED%81%AC%EC%84%9C%EB%B9%84%EC%8A%A4-SW-QA-%EC%97%94%EC%A0%9C%EB%8B%88%EC%96%B4-%EA%B3%B5%EC%B1%84-%EB%A9%B4%EC%A0%91-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@bxxloob_-/%EB%A9%B4%EC%A0%91%ED%9B%84%EA%B8%B0-%EC%97%94%ED%85%8C%ED%81%AC%EC%84%9C%EB%B9%84%EC%8A%A4-SW-QA-%EC%97%94%EC%A0%9C%EB%8B%88%EC%96%B4-%EA%B3%B5%EC%B1%84-%EB%A9%B4%EC%A0%91-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Sat, 14 Sep 2024 06:30:01 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/0ca338df-8b25-402e-a7a5-67e67bb4bbe9/image.png" alt=""></p>
<p>기존의 엔테크서비스 QA 채용 프로세스는 이러하다 </p>
<blockquote>
<p>[서류] - [Pre-test] - [면접] - [최종합격]</p>
</blockquote>
<p>그러나 이번 채용부터 면접의 방식을 변경하여 아래와 같이 진행한다고 한다. </p>
<blockquote>
<p>[서류] - [Pre-test] - [1차 면접] - [2차 면접] - [최종합격]</p>
</blockquote>
<p>작성 기준에서는 1차 면접도 합격하여 2차면접을 앞두고 있는 상황이라 면접에 관련된 내용은 삼가하고 면접에서 받은 피드백을 좀 정리하고자 하여 작성한다.</p>
<p>QA 직군으로서는 정확히 두번째 면접이다. 첫번째 면접은 펄어비스 QA 비기너 인턴쉽 면접을 처음으로 봤고, 이어 엔테크서비스가 두번째이다. </p>
<p>면접은 면접관 3: 1로 30분간 진행됐고, 수내 사무실에서 진행됐다 . </p>
<h1 id="느낀점-및-피드백">느낀점 및 피드백</h1>
<h2 id="역량의-객관적-지표">역량의 객관적 지표</h2>
<p>우선 면접이 끝나고 복기하면서 느낀 점은 , 내가 부족한 것이 무엇이고 어떤 상태인지 <strong>객관적</strong> 으로 판단할 수 있는 시간이였다는 것이다. </p>
<p>회사마다, 또한 면접관님들마다 원하는 역량과 요소가 다를 것이고, 내가 가지고 있고 준비한 능력이 당사와 이해관계가 맞아 떨어지면 면접에 통과할 수 있을 것이다. </p>
<p>그러다보니,, 회사에 맞는 인재를 뽑는 자리인만큼 회사에 대해 얼마나 알고 어떤 역량이 부족한지 흐름적으로 알 수 있게 된다. 가장 객관적인 것은 특정 역량에 대해 질문했을 때 답을 못하는 것일다. 나 또한 여러 질문에 답을 못했으며, 이에 대한 부족함을 알아차리게 됐다. </p>
<p>이러한 역량 부족을 파악하고, 모자란 부분을 채워가는게 면접의 긍정적 결과인 것 같고, 
단번에 부족한 역량을 판단할 수 있는 하나의 매체라고 생각하면 면접도 겁나지 않을 것 같다. </p>
<h2 id="자소서-및-면접-방식은-담백하게">자소서 및 면접 방식은 담백하게</h2>
<p>나는 최근까지 플러터 개발자로서 업무를 수행했다. 또한 회사에서도 테스트 관련 업무를 잠깐 수행했었다. 그렇다보니 회사에서 진행했던 테스트 프로세스를 엄청나게 부풀리고 거추장스럽게 자소서를 작성하고, 이를 바탕으로 면접을 봤었다. </p>
<p>면접 진행 중 이전 회사에서 경험한 테스트를 이야기했고, 그에 대해 자세하게 말씀해달라해서 내가 생각하고 준비했던대로 이야기를 했다. 그러나 바로 이에대한 피드백이 돌아왔다. </p>
<blockquote>
<p>&quot;다 좋은데, 지금 지원자님께서 있어보이고 그럴싸한 단어들만 사용하고 있다. 이러면 면접에 있어 부정적인 영향을 줄 것이고, 확실하지 않은 단어들이면 제외시키는 것이 좋을 것이다&quot;</p>
</blockquote>
<p>라는 냉철한 피드백이 돌아왔다. </p>
<p>집에와서 현재 면접을 복기하며 자소서를 다시 보고 면접 내용을 생각해보니 난 이런 뉘앙스의 면접을 진행했던 것 같다 .</p>
<blockquote>
<p>나는 이정도 공부했고, 되게 그럴싸한 사람이니까 제발 뽑아주세요 !!!!</p>
</blockquote>
<p>하지만 이는 면접관님들께 쉽게 간파당하였고, 어설픈 자신감은 오히려 독이 되었던 것 같다 .</p>
<p>아직 2차면접을 진행하지 않은 상태이고 2차 면접 결과가 어떻게 될진 모르겠지만, </p>
<p>위의 피드백과 느낀점을 토대로 2차 면접을 다시, 그리고 제대로 준비하여 최종합격에 들 수 있게 해야겠다 . </p>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/76c514b8-eea3-4f09-9724-eda3cd66dd95/image.png" alt=""></p>
<p>(면접끝나고 찍은 사무실 앞 사진)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ISTQB 합격 후기 feat. 공부방법 및 나만의 팁]]></title>
            <link>https://velog.io/@bxxloob_-/ISTQB-%ED%95%A9%EA%B2%A9-%ED%9B%84%EA%B8%B0-feat.-%EA%B3%B5%EB%B6%80%EB%B0%A9%EB%B2%95-%EB%B0%8F-%EB%82%98%EB%A7%8C%EC%9D%98-%ED%8C%81</link>
            <guid>https://velog.io/@bxxloob_-/ISTQB-%ED%95%A9%EA%B2%A9-%ED%9B%84%EA%B8%B0-feat.-%EA%B3%B5%EB%B6%80%EB%B0%A9%EB%B2%95-%EB%B0%8F-%EB%82%98%EB%A7%8C%EC%9D%98-%ED%8C%81</guid>
            <pubDate>Thu, 05 Sep 2024 15:05:32 GMT</pubDate>
            <description><![CDATA[<p>자격증은 이미 정처기 / SQLD를 갖고 있지만, 처음으로 합격 후기를 해봅니다!
8월 29일 목요일 삼성역에서 7시반 시험을 봤습니다.</p>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/55f7597e-d366-4039-ad41-184bdf7a46d1/image.png" alt=""></p>
<h2 id="취득-목적">취득 목적</h2>
<p>본격적인 후기에 들어가기 전에, 취득 목적에 말씀드리겠습니다. 저는 컴퓨터 관련 공학과를 졸업하고 1년간 개발자(Flutter)로 업무를 진행하다 퇴사 후 SQA로 새로운 출발을 준비하고 있는 상태입니다. 또한 주변에 QA 관련 일을 하시는 분을 알지도 못했을 뿐만 아니라 QA팀이나 품질 관련 유관부서가 있는 팀이 아니였기에 어떻게 준비해야하지 막막함이 앞섰습니다.</p>
<p>그러다 알게된게 ISTQB이고, 이 자격증은 소프트웨어의 전반적인 프로세스나 기법, 생태계 등을 익히는데 쉽다 하여 취득하기로 마음을 먹었습니다.</p>
<br>


<h2 id="공부방법">공부방법</h2>
<p>저는 약 1달 반 정도의 시간이 있었고, 기초부터 탄탄히 쌓자는 마인드로 공부를 시작했습니다.</p>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/d0c6f707-19d3-4b59-9a08-e3095ecedef4/image.png" alt=""></p>
<p>저는 우선 개발자도 알아야할 소프트웨어 테스팅 실무 제3판(일명 &quot;개알&quot;)로 공부를 처음에 시작했습니다. 물론 ISTQB 시험범위는 sten에 있는 실라버스에서만 문제가 출제되는 것이기 때문에 해당 책을 볼 필요는 없었지만, 자격증 취득에 목적을 둔 것이 아니라 QA 관련 업무나 스킬을 전체적으로 공부할 수 있다고 하여 구매하여 읽기 시작했습니다. </p>
<p>전공자이며 뛰어나진 않지만 1년간 개발자로 살아온 만큼 (+정처기 취득) 쉽게 읽힐 것이라고 생각했지만, 생각보다 집중력과 이해력이 요구되는 책이였으며, 시험 준비에 시간이 많으신 분들은 꼭 1<del>2번 정독하셨으면 좋겠습니다. (저도 정독 2번 이후에 쳐다도 안봤습니다 ^</del>^..)</p>
<br>




<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/097fc737-77ac-469a-84ce-c1b4dbc962b0/image.png" alt=""></p>
<p>그 다음으로는 문제로 배우는 소프트웨어 테스팅 2(일명 &quot;문배&quot;)로 ISTQB 시험 흐름을 읽혔습니다.과거 ISTQB는 이런식으로 출제가 되었었고, 어떤 성향을 띄는지 확인하기 좋다해서 시작했습니다. </p>
<p>체감상 ISTQB 실제 시험보다 훨~씬 쉬운 문제들입니다. 책 뒤에 해설이 있어 내가 부족한 파트가 무엇인지 상기할 수 있는 책이였습니다. </p>
<p>근데 저는 이 책 다 안풀었습니다 ㅋㅎ.. 시험 일주일 전에 책을 주문해서 2일간 모의고사랑 6단원 뺴고 다 풀고 1~5단원만 선지분석 및 오답정리를 진행하였습니다. </p>
<br>

<p>마지막으로는 실라버스 &amp; 샘플문제입니다.</p>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/1284a937-f957-41c9-8a3c-6ee3141346b0/image.png" alt=""></p>
<p>객관적이고 주관적으로 시험 합격에 가장 필요한 친구들입니다. </p>
<p>실제 시험에서 샘플문제와 비슷한 유형의 문제들도 나왔고, 착각일 수 있겠지만 거의 동일하다고 생각이 들정도로 문제와 선지가 똑같았던 문제가 있었던걸로 기억합니다.(말장난하기에 좋은 문제일 수도 있으니 쉽더라도 문제/선지 분석 및 파악이 중요하겠죠?)</p>
<p>실라버스를 처음 정독하고 샘플문제A를 처음 풀었을 때 20개를 맞았던 기억이 있습니다. 그만큼, 내가 아는 것과 문제를 푸는 것은 많이 다를거예요. 그렇게 느낀게, 아는 것과 많이 다르기도 하지만 실라버스에서 중요하다고 생각 안했던 부분들도 문제를 내기도 하고 간단히 언급하고 지나갔던 문제들도 길게 풀면 어려워지기도 하더라구요. 주의하시길 바랍니다. 기억나는건 샘플문제 A에 있는 전체적 팀 접근 문제 였습니다 . (하단에 올려놓을게요) 제가 이 부분을 쉽다고 생각하고 넘어갔는데, 문제로 나와버리니까 하나도 기억안나고 바로 틀렸습니다 .. </p>
<p>(샘플문제 A 8번 문제)</p>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/60ad2233-89bc-4963-b538-b95e1f8a7912/image.png" alt=""></p>
<p>저는 시험 2일전 A,B 문제 -&gt; 오답 
하루전 C,D 문제 -&gt; 오답
당일 -&gt; 오답보면서 시험장 이동 </p>
<p>이런식으로 했던 것 같습니다. </p>
<br>


<h2 id="정리-및-팁">정리 및 팁</h2>
<p>제가 생각했을 때, 이 시험에 핵심은 <strong>단어의 뜻이나 프로세스 별 과정 및 활동 등 모든 것들을 정확히 이해하고 암기하자</strong> 입니다. </p>
<p>제가 공부했던 방식은 
[개알 정독1] - [실라버스 정독1] - [개알 정독2] - [실라버스 정독2] - [문배 각 단원] - [실라버스 각 단원 복습  및 오답] - [샘플문제] - [오답 및 실라버스 복습 2] </p>
<p>이런식으로 진행했습니다. 개알과 문배는 과거 실라버스를 기준으로 작성된 책이며, 올해 24년 초에 개정된 ISTQB 실라버스와 상이한 내용을 담고있는 경우도 있습니다. </p>
<p>이렇듯 현재 실라버스를 기준으로 암기를 하되, 과거 실라버스와 비교를 통해 암기를 하고, 오답을 하면서 부족한 부분을 채워나가면 될 것 같습니다. </p>
<p>특히 오답이 정말 중요한 것 같습니다. ISTQB 시험을 치뤄 본 결과, 말장난이 정말 심하기 때문에, ㅏ 다르고 ㅓ 다른 것들을 잘 파악하여 정확히 이해하는 것이 중요하겠습니다.</p>
<p>암기할 때 다들 쉬운방법으로 암기하고, 손으로 써가며 각 과정엔 어떤 활동들이 있고 어떤 활동들이 겹치는지 암기하는 것 또한 중요합니다. </p>
<p>아무튼 9시 발표인데 9시에 바로 발표를 안해줘서 시험지 및 답안지에 이름이나 마킹 실수를 했나 싶어서 5분간 엄청 떨었네요. </p>
<p>다들 합격하시는데 도움이 되었으면 좋겠습니다 .  감사합니다 !</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[QA] JIRA와 애자일 1 - 에픽과 사용자 스토리 연결]]></title>
            <link>https://velog.io/@bxxloob_-/QA-JIRA%EC%99%80-%EC%95%A0%EC%9E%90%EC%9D%BC-1-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%8A%A4%ED%86%A0%EB%A6%AC-%EC%9E%91%EC%84%B1-%EC%9A%94%EB%A0%B9</link>
            <guid>https://velog.io/@bxxloob_-/QA-JIRA%EC%99%80-%EC%95%A0%EC%9E%90%EC%9D%BC-1-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%8A%A4%ED%86%A0%EB%A6%AC-%EC%9E%91%EC%84%B1-%EC%9A%94%EB%A0%B9</guid>
            <pubDate>Thu, 01 Aug 2024 11:17:23 GMT</pubDate>
            <description><![CDATA[<h3 id="사용자-스토리-작성">사용자 스토리 작성</h3>
<p>&quot;누가, 무엇을 하고자 하며, 그래서 얻는 이득은 무엇인가?&quot;
-&gt; &quot;As a [Persona], I [want to], [so That]&quot;</p>
<p>이러한 글은 고객의 관점에서 작성되어야한다.</p>
<p>가령 신용카드 뱅킹의 앱이라고 가정하면,
&quot;신용카드 온라인 뱅킹 고객으로서 저는 사용자 이름과 비밀번호를 만들어 포털에 자격 인증서를 제공하고싶다.&quot;</p>
<p>이런식으로 작성해야, 고객입장에서 </p>
<ol>
<li>&quot;누가&quot;(신용카드 온라인 뱅킹 고객) </li>
<li>&quot;무엇을 하고자&quot;(사용자 이름과 비밀번호를 만들어) </li>
<li>&quot;그래서 얻는 이득은 무엇&quot;(포털에 자격 인증서를 제공하고싶다)</li>
</ol>
<p>무엇을 하고싶고, 결과로 어떤것을 원하는지 알 수 있다. </p>
<p> </p>
<br>


<p>결국 해당 프로젝트에서 하고자 하는 것들은 애자일 기법 중 스크럼이기 떄문에,</p>
<p>하단 이미지같이 <strong><strong>에픽 / 스토리 / 태스크</strong></strong>를 이해하면서 진행할 것이다.</p>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/81a761d4-8eb0-48fa-a0fe-7fdd8c8e0fc8/image.png" alt=""></p>
<br>

<h3 id="에픽-및-스토리-생성">에픽 및 스토리 생성</h3>
<ol>
<li>우선 생성한 JIRA 프로젝트에 들어가서 _에픽_을 만들어줘야한다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/8184047d-1bb1-41c5-a3f7-1626369ea9fb/image.png" alt=""></p>
<br>


<ul>
<li>좌측 카테고리에서 백로그를 누르고, <em>Epic 만들기</em> 버튼을 눌러 만든다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/98da0ce4-337f-4d1e-bdde-8901672cffc0/image.png" alt=""></p>
<br>

<p>버튼을 누르면 나타나는 화면의 모습. 개인의 상황에 맞게 기입을 해주면된다. </p>
<br>


<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/e6c366ab-cacc-43d2-aa38-388da1b0a56f/image.png" alt=""></p>
<p>기입후에 완성한 모습.</p>
<p>이제 Epic은 만들었으니, 위의 스크럼의 구성요소의 사진처럼, 에픽을 구성하는 <em>스토리</em>를 만들어야한다. </p>
<br>


<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/eb64a452-28d9-49e3-948f-6fe179628712/image.png" alt=""></p>
<p>박스친곳을 유념하여 만들어야한다.</p>
<ol>
<li>이슈유형에 스토리를 클릭하여야한다.</li>
<li>요약에 해당 스토리가 어떤 스토리인지 요약하여 서술(스토리 이름? 같은 느낌)</li>
<li>상위항목은 어떤 에픽인지 설정</li>
<li>우선순위 설정</li>
<li>레이블 선택 (첫 에픽 or 첫 스토리면 레이블이 없을 것이고, 저기에 직접 타이핑하여 Enter를 치면 자동으로 알아서 첫 레이블이 기입됨.)</li>
<li><del>또한 하단에 담당자도 있어, 누구 진행할지 선택할 수 있는데 , 해당 캡쳐본에서는 너무 길어 짤렸음을 이해해주길 바람</del></li>
</ol>
<br>


<p>이렇게 하면 첫 에픽의 첫 스토리 작성이 완료된다.</p>
<br>

<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/6db01f3b-c784-4370-9df3-e87429a08367/image.png" alt=""></p>
<br>


















<p>[출처] <a href="https://coding-food-court.tistory.com/146#google_vignette">https://coding-food-court.tistory.com/146#google_vignette</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ISTQB 1단원]]></title>
            <link>https://velog.io/@bxxloob_-/ISTQB-1%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bxxloob_-/ISTQB-1%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Mon, 10 Jun 2024 12:54:49 GMT</pubDate>
            <description><![CDATA[<h2 id="1-실라버스-목차-및-개념-단어-확인">1. 실라버스 목차 및 개념, 단어 확인</h2>
<h3 id="목차">목차</h3>
<blockquote>
<ol start="0">
<li>서론</li>
</ol>
</blockquote>
<ol>
<li>테스팅의 기초</li>
<li>소프트웨어 개발수명주기(SDLC)와 테스팅</li>
<li>정적 테스팅</li>
<li>테스트 분석과 설계</li>
<li>테스트 활동 관리</li>
<li>테스트 도구</li>
<li>References</li>
</ol>
<h3 id="지식-수준-단어">지식 수준 단어</h3>
<p>K1 : 기억 (Remember)
K2 : 이해 (Understand)
K3 : 적용 (apply)</p>
<h3 id="출제범위">출제범위</h3>
<p>문제는 6개의 장 내용에서 출제. 각 장의 최상위 제목에서는 한 장 당 시간을 지정한다. 시간 정보는 장 레벨 이하에서는 제공되지 않는다. 인증 교육과정의 경우, 실라버스는 1135(18시간 55분)의 강의가 필요하며, 다음과 같이 6개의 장에 분산되어 있음.</p>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/e500f8cd-9b0a-4446-a8bc-6c794c5a07d8/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/e2c9378a-3d3c-4fe5-8645-774b796b7658/image.png" alt=""></p>
<h2 id="2-istqb-1장-테스팅의-기초">2. ISTQB 1장 테스팅의 기초</h2>
<p>[[5가지의 소단원]]</p>
<blockquote>
<p>1 테스팅이란 무엇인가
2 테스팅이 왜 필요한가?
3 테스팅 원리
4 테스트활동, 테스트웨어, 테스트 역할
5 테스팅 필수 기술 및 모범 사례</p>
</blockquote>
<h3 id="1-테스팅이란-무엇인가">1. 테스팅이란 무엇인가</h3>
<ul>
<li>결함을 식별하고 스프트웨어 산출물의 품질을 평가하는 일련의 활동. </li>
<li>테스트의 대상이 되는 이런 산출물을 테스트. </li>
<li>테스팅 = 요구사항을 충족하는지 확인하는 Verification + 사용자 또는 기타 이해관게자가 필요한 바를 만족하는지를 확인하는 Validation</li>
<li>테스팅 = 정적(static, 리뷰 + 정적분석) + 동적(dynamic, 소프트웨어를 실행하여 직접 진행.)</li>
<li>ISO/IEC/iEEE 29119-1 표준 -&gt; 소프트웨어 테스팅 개념에 대한 추가 정보를 제공.</li>
</ul>
<h3 id=""></h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[[flutter] 제약조건 이해하기]]></title>
            <link>https://velog.io/@bxxloob_-/flutter-%EC%A0%9C%EC%95%BD%EC%A1%B0%EA%B1%B4-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@bxxloob_-/flutter-%EC%A0%9C%EC%95%BD%EC%A1%B0%EA%B1%B4-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 04 Nov 2023 05:48:06 GMT</pubDate>
            <description><![CDATA[<h2 id="0--작성배경">0.  작성배경</h2>
<br>



<p>플러터를 개발하다보면 아래와 같은 문구를 보게된다. </p>
<blockquote>
<p>Constraints go down. Sizes go up. Parent sets position.
 =&gt; 제약 조건이 아래로(하위로). 사이즈는 위로(상위로). 부모(상위)는 위치를 설정합니다.</p>
</blockquote>
<br>

<p>플러터를 배운지 이제 막 한달을 넘긴 상태에서, 저러한 문구는 많은 블로그에서 보게 된다. 
그러나 처음에 찾아볼 때는 그래도 당연한 것이라고 생각하고 대수롭지 않게 이해하고 넘어갔다. </p>
<p>그러나 플러터를 깊게 공부하기 시작하면서 다시 한번 상기하고자 혹은 깊게 이해하고 작성하게 되었다.</p>
<br>

<h2 id="1-찾아보게된-계기">1. 찾아보게된 계기</h2>
<br>


<p>우선 플러터로 가벼운 앱을 하나 개발중인데, 아래와 같은 문제에 봉착했다.</p>
<blockquote>
<p>본인 : 윈도우 PC로 안드로이드로 개발중인데, 맥북으로 테스트해보고자 켰지만 SafeArea + 기기차이로 인해 위젯이 깨지거나 원하는 상태로 나타나지 않은 상태.</p>
</blockquote>
<br>

<p>그렇게해서 SafeArea를 적용했을 때와 안했을 때의 차이와 두 상황에 따라 동적으로 위젯의 사이즈를 조절할 수 있으면 좋겠다해서 찾아보게 됐음</p>
<br>


<h2 id="2-이해">2. 이해</h2>
<p>우선 위젯이 가지는 크기와 제약조건을 알아야한다.
아래는 flutter docs에서 가져온 원문과 파파고 감성 낭낭한 해석본이다.</p>
<blockquote>
<p>A widget gets its own constraints from its parent. A constraint is just a set of 4 doubles: a minimum and maximum width, and a minimum and maximum height.</p>
<p>Then the widget goes through its own list of children. One by one, the widget tells its children what their constraints are (which can be different for each child), and then asks each child what size it wants to be.</p>
<p>Then, the widget positions its children (horizontally in the x axis, and vertically in the y axis), one by one.
And, finally, the widget tells its parent about its own size (within the original constraints, of course).</p>
</blockquote>
<blockquote>
<p>위젯은 부모로부터 자신만의 제약조건을 얻습니다. 제약조건은 최소 너비와 최대 너비, 최소 높이, 최대 높이 인 4개의 doubles조건을 받는다.</p>
<p>그런다음 위젯은 자신의 자식리스트들을 통과한다. 위젯은 자신의 제약 조건이 무엇인지 (자식 위젯마다 다를 수 있음)를 아이들에게 하나씩 알려준 다음 각 자식위젯에게 자신의 크기가 무엇인지 물어본다.</p>
<p>그런 다음 위젯은 자식(가로로 x축, 세로로 y축)를 하나씩 배치한다.
그리고 마지막으로 위젯은 자신의 크기(물론 원래 제약 조건내)에 대해 부모에게 알려줍니다.</p>
</blockquote>
<br>

<p>글만 읽으면 이해가 안되니, 예를 들어보자.</p>
<p>** 해당 위젯은 패딩이 있는 열이 포함되어 있고, 두 하위 항목을 다음과 같이 배치하려는 경우 **</p>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/0503cdc5-3159-41a5-9db4-c67181eb56a8/image.png" alt=""></p>
<br>

<p>위와 같은 레이아웃을 가지려면 아래와 같은 순서를 가지며 렌더링된다.</p>
<pre><code>해당 위젯: &quot;부모님, 제 제약 조건은 무엇입니까?&quot;

부모 위젯: &quot;너는 너비가 80에서 300 픽셀, 키가 30에서 85 픽셀이어야 합니다.&quot;

위젯: &quot;음, 저는 5픽셀의 패딩을 갖고 싶기 때문에, 제 아이들은 최대 290픽셀의 폭과 75픽셀의 높이를 가질 수 있습니다.&quot;

위젯: &quot;첫째 자식위젯, 너는 너비가 0에서 290픽셀, 키가 0에서 75여야 해.&quot;

첫째 위젯: &quot;좋아요, 그럼 저는 가로 290픽셀, 세로 20픽셀이면 좋겠어요

위젯: &quot;음, 저는 둘째 아이를 첫째 아이보다 아래에 두고 싶기 때문에, 이것은 둘째 아이에게 55픽셀의 키만을 남깁니다.&quot;

위젯: &quot;이봐 둘째, 너는 폭이 0에서 290, 키가 0에서 55여야 해&quot;

둘째 위젯: &quot;좋아요, 저는 가로 140픽셀, 세로 30픽셀이면 좋겠어요.&quot;

위젯: &quot;아주 좋아요. 첫째 아이는 x:5, y:5 위치이고, 둘째 아이는 x:80, y:25 위치입니다.&quot;

위젯: &quot;부모님, 제 사이즈는 가로 300픽셀, 세로 60픽셀로 결정했습니다.&quot;</code></pre><p>정리하면 아래와 같다.</p>
<ol>
<li>부모 위젯이 자식위젯에게 크기 및 크기제약(constraint)를 부여한다.</li>
<li>자식 위젯은 그 제약 내에서 자신의 배치 방식을 결정한다.</li>
</ol>
<br>





<p><del>작성중..</del></p>
<br>

<br>

<br>



<p>[Flutter Docs] <a href="https://docs.flutter.dev/ui/layout/constraints">https://docs.flutter.dev/ui/layout/constraints</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[flutter] FCM fore/background 별 메세지 받기]]></title>
            <link>https://velog.io/@bxxloob_-/flutter-FCM-forebackground-%EB%B3%84-%EB%A9%94%EC%84%B8%EC%A7%80-%EB%B0%9B%EA%B8%B0</link>
            <guid>https://velog.io/@bxxloob_-/flutter-FCM-forebackground-%EB%B3%84-%EB%A9%94%EC%84%B8%EC%A7%80-%EB%B0%9B%EA%B8%B0</guid>
            <pubDate>Thu, 26 Oct 2023 05:04:40 GMT</pubDate>
            <description><![CDATA[<p>Firebase에서 메시지 처리는 플러터 앱에서 다양한 상황에 따라 동작합니다. 코드를 자세히 살펴보겠습니다.</p>
<p>FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { ... }): 이 부분은 앱이 백그라운드에서 실행 중일 때 FCM 메시지를 수신하면 해당 리스너의 코드가 실행됩니다. 이 코드는 &quot;A new onMessageOpenedApp event was published!&quot;를 출력하는 간단한 로그 메시지를 표시합니다. 즉, 백그라운드에서 메시지를 받았을 때 실행됩니다.</p>
<p>FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler): onBackgroundMessage는 백그라운드에서 FCM 메시지를 처리하는 핸들러를 등록하는 부분입니다. _firebaseMessagingBackgroundHandler 함수는 백그라운드 메시지를 처리하며, 여기서 onBackgroundMessageData라는 변수를 사용하여 메시지 데이터를 로깅합니다.</p>
<p>결론적으로, 위의 코드에서 백그라운드에서 FCM 메시지를 받을 때 onMessageOpenedApp 리스너에 있는 로직은 동작하지 않습니다. 대신, _firebaseMessagingBackgroundHandler 함수가 백그라운드에서 동작합니다.</p>
<p>onMessageOpenedApp는 앱이 포그라운드에서 실행 중인 경우 또는 사용자가 백그라운드 알림을 탭하여 앱을 다시 열었을 때 호출됩니다. 백그라운드에서 메시지를 처리하려면 onBackgroundMessage와 같이 onMessage 대신 onMessageOpenedApp를 사용해야 합니다. 이렇게 하면 백그라운드 및 포그라운드에서 메시지를 처리할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[flutter] FCM 메세지 구조 및 전달]]></title>
            <link>https://velog.io/@bxxloob_-/flutter-FCM-%EB%A9%94%EC%84%B8%EC%A7%80-%EA%B5%AC%EC%A1%B0-%EB%B0%8F-%EC%A0%84%EB%8B%AC</link>
            <guid>https://velog.io/@bxxloob_-/flutter-FCM-%EB%A9%94%EC%84%B8%EC%A7%80-%EA%B5%AC%EC%A1%B0-%EB%B0%8F-%EC%A0%84%EB%8B%AC</guid>
            <pubDate>Thu, 26 Oct 2023 02:22:17 GMT</pubDate>
            <description><![CDATA[<p>우선 Firebase Cloud Messaging에서 사용되는 구조를 알아보자</p>
<br>




<h3 id="1-구조">1. 구조</h3>
<br>





<pre><code class="language-dart">{
  &quot;message&quot;: {
    &quot;token&quot;: &quot;bk3RNwTe3H0:CI2k_...&quot;,
    &quot;notification&quot;: {
      &quot;title&quot;: &quot;Breaking News&quot;,
      &quot;body&quot;: &quot;New news story available.&quot;
    },
    &quot;data&quot;: {
      &quot;story_id&quot;: &quot;story_12345&quot;
    }
  }
}
</code></pre>
<br>




<p>보통은 이러한 형식의 JSON구조를 가진다.</p>
<ul>
<li>message &gt; token , notification, data</li>
</ul>
<br>





<p>우선 내가  FCM서버와 연결하여 사용중인 스프링부트 서버(푸시 서버)에 작성한 코드를 확인해보자</p>
<pre><code class="language-java">public void sendPush(String tokenValue) throws FirebaseMessagingException {
        Notification notification = Notification.builder()
                .setTitle(&quot;hello world&quot;)
                .setBody(&quot;Send Test Messages&quot;)
                .build();

        Message message = Message.builder()
                .setToken(tokenValue)
                .setNotification(notification)
                .putData(&quot;Hello&quot;, &quot;EveryOne!&quot;)
                .build();

        System.out.println(&quot;Send FCM for Server &quot; + tokenValue);

        firebaseMessaging.send(message);
    }</code></pre>
<br>


<h3 id="2-전달">2. 전달</h3>
<br>




<p>해당 코드를 보면 우선 Notification인스턴스를 builder를 통해 만든다. 해당 인스턴스의 필드들을 보면 title과 body가 존재. </p>
<br>


<p>이게 실질적으로 우리가 보통 핸드폰이나 모바일기기에서 푸시 알람(=FCM 메세지, 메세지) 받을 때 나오는 내용들이다
즉, Notification은 푸시 알람에 대한 정보 및 푸시 알람의 표시에 사용되는 정보들이 담겨있다고 보면 된다. </p>
<br>

<p>그 다음 코드를 보면 Message 클래스의 인스턴스를 만드는 것으로 보인다. 
해당 인스턴스에는 token과 notification을 set하는 부분이 있다. </p>
<p>FCM 특성상, 모바일 기기별로 토큰값이 달리하고, 특정 기기를 타겟으로 푸시 알람을 보내야하기 떄문에, 해당 메세지에 토큰값을 설정한다. </p>
<p>또한 setNotification에다가는 우리가 앞서 만든  Notification의 인스턴스를 넣게된다. </p>
<br>




<p>마지막으로 putData를 통해 실질적으로 푸시 서버에서 FCM서버로 보내고싶은 실제 정보들(update하고 싶은 값이나, 새로 넣고 싶은 값들?)을 넣고 보낸다. 나는 여기서 임시예제로 보여주기 위한 값들을 넣음.</p>
<p>그렇게 하고 <code>firebaseMessaging.send(message)</code>를 하면 해당 FCM서버에 메세지가 전송되고, 전송된 메세지는 FCM서버에서 해당 토큰값에 맞게 메세지를 전달한다. </p>
<br>

<h3 id="3-결론">3. 결론</h3>
<br>


<p>메시지의 구조는 FCM 메시지를 위한 JSON 구조와 유사함. 따라서 Notification과 Message의 사용은 FCM 메시지의 notification 및 message 부분과 일치. 이렇게 설정된 메시지는 FCM 서버를 통해 해당 토큰 값을 가진 기기로 전달될 것이며, 기기에서는 푸시 알림이 표시되고 푸시 메시지의 내용이 표시될 것.</p>
<p>따라서 서버에서 메시지를 구성하고 FCM 서버로 보내면, 이러한 메시지가 원격 기기로 전달되는 것이 FCM의 작동 원리입니다.</p>
<br>


<h3 id="4-결과">4. 결과</h3>
<p><del>플러터에서 작성된 디버깅용 임시 코드입니다</del></p>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/0e132cda-7248-45bd-81ed-f04049c56bba/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/77f588e0-254d-4407-8c78-ce083232e062/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/7592a77f-c6c5-406b-b4dc-9a260ef5d958/image.png" alt=""></p>
<br>




<br>




<p>[Referrence] <a href="https://team-platform.tistory.com/23">https://team-platform.tistory.com/23</a>
[Firebase Docs] <a href="https://firebase.google.com/docs/cloud-messaging/flutter/receive?hl=ko">https://firebase.google.com/docs/cloud-messaging/flutter/receive?hl=ko</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[flutter] Firebase 백그라운드 일 때 initializeApp]]></title>
            <link>https://velog.io/@bxxloob_-/flutter-Firebase-%EB%B0%B1%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9C-%EC%9D%BC-%EB%95%8C-initializeApp</link>
            <guid>https://velog.io/@bxxloob_-/flutter-Firebase-%EB%B0%B1%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9C-%EC%9D%BC-%EB%95%8C-initializeApp</guid>
            <pubDate>Tue, 24 Oct 2023 02:11:20 GMT</pubDate>
            <description><![CDATA[<br>




<pre><code class="language-dart">Future&lt;void&gt; main() async {

...
await FCMService().init();

FirebaseMessaging.onBackgroundMessage(
      _firebaseMessagingBackgroundHandler); //TOP-Level

....
}


Future&lt;void&gt; _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);

  debugPrint(&#39;A background message just showed up :  ${message.messageId}&#39;);

}
...
</code></pre>
<p>이렇게 작성된 코드가 있는데, 이에 대해서 설명해줘. 무엇을 하기 위한 코드고 어떻게 동작하고 어떠한 원리를 갖고있는지 궁금했다.</p>
<br>




<h3 id="나의-이해">나의 이해</h3>
<p>내가 이해한것은 우선은 이러하다. </p>
<ul>
<li>우선 플러터의 진입점인 main()함수에서 <code>await FCMService().init();</code>를 통해 커스텀해놓은 FCM 앱을 초기화하고 설정한다.
( FCMService.init()안에는 당연하게도 <code>Firebase.initializeApp</code>코드가 존재. )</li>
</ul>
<p>앱이 실행하면서 실행한 디바이스 및 기기에 대한 토큰값을 받아오고, FCM에 대한 초기화를 진행하는 코드라고 생각했다 </p>
<ul>
<li>그러다 백그라운드(앱이 실행중이지만, 화면에 떠있지 않는 상태) 일 때 <code>_firebaseMessagingBackgroundHandler</code>를 실행한다</li>
</ul>
<p>하지만 여기서부터 궁금했던 것이 생긴다.</p>
<br>

<h3 id="궁금점">궁금점</h3>
<p><strong>이미 초기화를 했는데, 왜 다시 백그라운드 일 떄 초기화할까?</strong></p>
<p>에 대한 궁금점이 생겨서 좀 찾아봤다.</p>
<h3 id="결론">결론</h3>
<br>

<blockquote>
<p>Firebase 초기화를 백그라운드에서 다시 실행시키는 이유는 Firebase 앱을 백그라운드 상태에서도 제대로 동작하도록 보장하기 위한 조치입니다. Firebase는 앱이 시작될 때 초기화되어야 하지만, 앱이 백그라운드에서 푸시 알림을 수신하거나 백그라운드 작업을 수행할 때 Firebase 기능을 계속 사용해야 합니다. Firebase는 이러한 상황에서도 제대로 동작하려면 Firebase 앱을 백그라운드에서 다시 초기화해야 합니다.</p>
</blockquote>
<p>일반적으로 앱이 포그라운드(화면이 표시되는 상태)일 때 Firebase가 초기화됩니다. 이때 Firebase는 사용자와 상호작용하며 푸시 알림을 표시하거나 데이터를 처리하는 등의 작업을 수행합니다. 그러나 앱이 백그라운드로 전환되면 일부 플랫폼에서 Firebase 초기화가 중지될 수 있습니다. 이때 백그라운드에서 Firebase 기능을 계속 사용하려면 Firebase를 백그라운드에서 다시 초기화해야 합니다.</p>
<blockquote>
</blockquote>
<p>따라서 await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); 코드는 백그라운드에서 Firebase를 다시 초기화하여 Firebase 기능을 계속 사용할 수 있도록 합니다. 이렇게 하면 백그라운드에서 푸시 알림을 정상적으로 수신하고 처리할 수 있으며, 필요한 경우 데이터 처리 및 작업을 수행할 수 있습니다.</p>
<blockquote>
</blockquote>
<p>즉, Firebase 초기화는 앱의 현재 상태에 따라 Firebase 서비스를 사용할 수 있도록 유지하는 데 필요한 조치입니다.</p>
<hr>
<p>[10/26 추가]</p>
<p>백그라운드에서 메세지를 처리하는 프로세스는 네이티브(Android 및 Apple) 플랫폼과 웹 기반 플랫폼에서 다릅니다.</p>
<p><strong>나는 보통 푸시 알람을 모바일 네이티브(Android 및 Apple)에서 사용하려고 하기 때문에, 웹은 배제하기로 한다.</strong></p>
<p>이 Android 및 apple 플랫폼에서 사용하려면 , onBackgroundMessage 핸들러를 등록하여 백그라운드 메시지를 처리한다.</p>
<p>여기서 주의사항으로는</p>
<blockquote>
<ol>
<li>익명 함수가 아니어야 한다.</li>
<li>최상위 수준 함수여야 한다.</li>
<li>Flutter 버전 3.3.0 이상을 사용하는 경우 메시지 핸들러는 함수 선언 바로 위에 `@pragma(&#39;vm:entry-point&#39;)로 주석을 달아야 한다(그렇지 않으면 출시 모드의 경우 트리 쉐이킹 중에 삭제 가능성이 있음)</li>
</ol>
</blockquote>
<pre><code class="language-dart">@pragma(&#39;vm:entry-point&#39;)
Future&lt;void&gt; _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  // If you&#39;re going to use other Firebase services in the background, such as Firestore,
  // make sure you call `initializeApp` before using other Firebase services.
  await Firebase.initializeApp();

  print(&quot;Handling a background message: ${message.messageId}&quot;);
}

void main() {
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
  runApp(MyApp());
}
</code></pre>
<blockquote>
<p>왜 최상위 수준에서 함수를 작성해야하고 익명함수가 아니어야 할까?
<br></p>
<p>❗ 핸들러는애플리케이션 컨텍스트 외부에서 독립적으로 실행되므로 애플리케이션 상태를 업데이트 하거나  UI에영향을 주는 로직을 실행할 수 없습니다. 그러나 HTTP요청과 같은 로직을 수행하고 IO작업(로컬스토리지업데이트)를 수행하며 다른 플러그인과 통신할 수 있다.</p>
<p>그렇기에 가능한 한 빨리 로직을 완료하는 것이 좋게, 집약적이로 오래 실행되는 태스크를 실행하면 기기 성능에 영향을 가기 때문에 30초가 넘게되면 자동으로 프로세스를 종료하는등, 애플리케이션의 Top-Level, 상위수준에서 로직이 이루어져야 한다. </p>
</blockquote>
<br>


<br>


<br>



<br>


<br>



<br>



<br>



<p>[Firebase Docs] <a href="https://firebase.google.com/docs/cloud-messaging/flutter/receive?hl=ko">https://firebase.google.com/docs/cloud-messaging/flutter/receive?hl=ko</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[flutter] RemoteMessage , RemoteNotification]]></title>
            <link>https://velog.io/@bxxloob_-/flutter-RemoteMessage-RemoteNotification</link>
            <guid>https://velog.io/@bxxloob_-/flutter-RemoteMessage-RemoteNotification</guid>
            <pubDate>Mon, 23 Oct 2023 07:39:15 GMT</pubDate>
            <description><![CDATA[<h3 id="1-remotemessage">1. RemoteMessage</h3>
<br>




<p>RemoteMessage 모델은 FCM 메시지 전체를 나타냅니다. 즉, 알림(Notification)과 데이터(Data) 모두를 포함합니다.
이 모델은 앱이 FCM 메시지를 수신했을 때 메시지의 모든 내용을 포함하며, 메시지 유형에 관계없이 항상 사용할 수 있습니다.
RemoteMessage에서는 notification 및 data 필드를 사용하여 알림과 데이터를 추출할 수 있습니다.</p>
<br>




<h3 id="2-remotenotification">2. RemoteNotification</h3>
<br>



<p>RemoteNotification 모델은 FCM 메시지에서 알림 (Notification) 부분을 나타냅니다.</p>
<ul>
<li>이 모델은 주로 FCM 메시지에 포함된 알림의 제목, 본문, 아이콘 및 기타 알림과 관련된 정보를 포함합니다.</li>
<li>RemoteMessage 객체 내부에서 notification 필드를 사용하여 알림 정보를 추출합니다.</li>
</ul>
<br>




<h3 id="3-결론">3. 결론</h3>
<br>





<p>따라서, RemoteMessage은 FCM 메시지의 전체 내용을 나타내고, RemoteNotification은 그 중에서 알림 부분만을 나타냅니다. 일반적으로 FCM 메시지를 처리할 때 RemoteMessage를 사용하여 알림 및 데이터를 모두 처리할 수 있습니다. 코드에서 message.notification은 RemoteNotification 객체를 반환하며, 이를 통해 알림 정보를 추출하고 처리할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[flutter] 비동기와 함께하는 BuildConext]]></title>
            <link>https://velog.io/@bxxloob_-/flutter-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%99%80-%ED%95%A8%EA%BB%98%ED%95%98%EB%8A%94-BuildConext</link>
            <guid>https://velog.io/@bxxloob_-/flutter-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%99%80-%ED%95%A8%EA%BB%98%ED%95%98%EB%8A%94-BuildConext</guid>
            <pubDate>Fri, 20 Oct 2023 02:40:13 GMT</pubDate>
            <description><![CDATA[<ol>
<li>riverpod 어노테이션 및 프로바이더를 사용하면 AutoDisposeNotifier를 extends하여 사용하게 되는데 이 때, build함수를 반드시 AutoDisposeNotifier에서 override하여 사용해야한다.
그러나 override하는 이 build함수에는 <strong>반드시 !! 동기적인 코드나 동기함수가 들어가야한다.</strong></li>
</ol>
<pre><code class="language-dart">myMind.build&#39; (&#39;Future&lt;bool&gt; Function()&#39;) isn&#39;t a valid override of &#39;AutoDisposeNotifier.build&#39; (&#39;bool Function()&#39;). (Documentation) 
The member being overridden (auto_dispose.dart:24).
</code></pre>
<p>대충 코드는 이러하다. </p>
<p>@riverpod으로 생성된 프로바이더 코드의 build함수에 Future<bool>로 리턴타입을 지정해줬는데, 위와 같은 오류가 나왔고, 찾아보니까 반드시 동기함수가 들어가야한다는 말이 있었다. </p>
<h2 id="1021-수정">[10/21 수정]</h2>
<p>  제목 : 10/20 오답노트 -&gt; [flutter] 비동기와 함께하는 BuildConext</p>
<p>  카페에 와서 공부를 하다가 어쩌다보니 비슷한 상황을 다른 블로그에서 찾게 되어 가지고 오게 됨.</p>
<p>  하단 출처의 docs를 읽어보면 아래와 같다 .</p>
<blockquote>
<p>세부 사항
비동기 공백을 가로질러 BuildContext를 사용하지 마십시오.</p>
<p>나중에 사용하기 위해 BuildContext를 저장하면 쉽게 진단하기 어려운 충돌이 발생할 수  있습니다. 비동기 공백은 암묵적으로 BuildContext를 저장하는 것이며 코드를 작성할 때 &gt; 간과하기 쉬운 일부입니다.</p>
<p>BuildContext를 사용할 때는 비동기 공백 후 마운트된 속성을 확인해야 합니다</p>
</blockquote>
<p>  정리를 해보면, buildContext를 비동기와 함께 사용하면 await 이후에 어디서 오류가 발생했는지 확인하기 힘들다고 하기 때문이다. </p>
<p>  또한 이 글을 보면서 하나 확인한것은, 그냥 문법적인 오류인줄 알았지만, 플러터쪽에서 제시한 lint에 해당하는 것이다. </p>
<p>  docs글 상단을 보면 <code>Tools
Linter &gt; rules&gt; &gt; use_build_context_synchronously</code>라고 카테고리를 나눈 것을 확인할 수 있다.</p>
<p>  플러터 기초를 학습하고 공부할 줄만 알았지 , 잊고 지냈던 lint가 문득 떠올라서 이를 맞춰가고 커스텀하며 사용해야겠다는 것을 깨달았다.</p>
  </br>

  </br>

  </br>




<p>  [reference-blog] : <a href="https://th-biglight.tistory.com/30">https://th-biglight.tistory.com/30</a>
  [flutter docs] :  <a href="https://dart.dev/tools/linter-rules/use_build_context_synchronously">https://dart.dev/tools/linter-rules/use_build_context_synchronously</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[flutter] provider관리 뷰? 뷰모델?]]></title>
            <link>https://velog.io/@bxxloob_-/flutter-provider%EA%B4%80%EB%A6%AC-%EB%B7%B0-%EB%B7%B0%EB%AA%A8%EB%8D%B8</link>
            <guid>https://velog.io/@bxxloob_-/flutter-provider%EA%B4%80%EB%A6%AC-%EB%B7%B0-%EB%B7%B0%EB%AA%A8%EB%8D%B8</guid>
            <pubDate>Wed, 18 Oct 2023 05:29:14 GMT</pubDate>
            <description><![CDATA[<p> 기능을 구현할 때, 어떻게 Provider를 관리할지에 대한 선택은 앱의 아키텍처 및 요구 사항에 따라 다를 수 있습니다. 일반적으로 상태 관리나 데이터 흐름을 조직화하기 위해 뷰모델(ViewModel)을 사용하는 것이 일반적입니다. 이를 통해 UI와 비즈니스 로직을 분리하고 데이터를 관리하거나 전달할 수 있습니다.</p>
<p>뷰모델을 사용하는 경우, getUpdateUserUseCaseImplProvider.notifier를 통해 데이터 업데이트에 관련된 로직을 뷰모델 내에서 처리하고, UI와 뷰모델 간에 데이터를 효율적으로 전달할 수 있습니다. 이로써 뷰와 로직을 분리하고, 코드를 더 쉽게 테스트하고 유지 보수할 수 있습니다.</p>
<p>예를 들어, 로그인 뷰모델에서 다음과 같이 사용자 업데이트를 관리할 수 있습니다:</p>
<pre><code class="language-dart">class LoginViewModel {
  final UpdateUserUseCase updateUserUseCase;

  LoginViewModel(this.updateUserUseCase);

  Future&lt;void&gt; updateUser(UserModel user) async {
    try {
      await updateUserUseCase.updateUser(user);
      // 업데이트 성공 시, UI를 업데이트하거나 다른 작업을 수행할 수 있음
    } catch (e) {
      // 오류 처리
    }
  }
}</code></pre>
<br>

<p>그러면 UI에서는 뷰모델을 생성하고 사용할 수 있습니다. UI는 사용자 입력을 수집하고, 이를 뷰모델에 전달하여 로직을 처리합니다.</p>
<pre><code class="language-dart">
final loginViewModel = LoginViewModel(getUpdateUserUseCaseImplProvider.notifier);

// ...

ElevatedButton(
  onPressed: () async {
    await loginViewModel.updateUser(user);
  },
  // ...
)</code></pre>
<p>이렇게 하면 비즈니스 로직과 UI가 분리되며, 뷰모델을 통해 데이터를 관리하고 UI를 업데이트할 수 있습니다. 이러한 구조는 앱의 복잡성이 증가할 때 특히 유용하며, 테스트와 유지 보수가 용이합니다.</p>
<p>또한 DIP(Dependency Inversion Principle, 의존성 역전 원칙)을 지킬 수 있다. 
View와 ViewModel은 서로 상호작용은 하지만, ViewModel은 전적으로 View의 존재를 모른다. View는 그저 상태관리를 ViewModel을 통해 전적으로 위임하며, ViewModel은 이에 맞게 데이터 관련 상태관리만 하게된다. 이렇게 관리된 상태값을 View에서는 그저 가져다 쓰기 떄문에 ViewModel은 전적으로 View의 존재를 모르게 된다. </p>
<p>이를 통해 상위 레벨에서 하위 레벨에 의존하지 않고, 유지보수성을 챙길 수 있다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[flutter] Factory Pattern(팩토리 패턴)]]></title>
            <link>https://velog.io/@bxxloob_-/flutter-Factory-Pattern%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@bxxloob_-/flutter-Factory-Pattern%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Wed, 27 Sep 2023 02:02:31 GMT</pubDate>
            <description><![CDATA[<br>
<br>

<h2 id="이론">이론</h2>
<br>
<br>

<p>우선 팩토리 패턴을 왜 쓰는지를 알고 기본을 잡아보자</p>
<p>(코드예제)</p>
<pre><code class="language-dart">factory Movie.fromJson(Map&lt;String, dynamic&gt; json) {

    adult: json[&quot;adult&quot;],
    backdropPath: json[&quot;backdrop_path&quot;] ?? &#39;&#39;,
    genreIds: List&lt;int&gt;.from(json[&quot;genre ids&quot;]

    ...
    ...

}</code></pre>
<p>명칭에 답이 있다. 팩토리와 패턴.
팩토리는 공장, 패턴은 어떠한 문제를 이미 검증된 솔루션(해결책)이 있어 규칙대로 만들면 문제를 해결 할 수 있는 것.</p>
<br>


<p><em>그렇다면 &#39; 어떠한 문제를 이미 검증된 솔루션의 규칙대로 문제를 해결 할 수 있는 공장&#39; 정도로 이해할 수 있겠다.</em></p>
<br>



<p>현실을 예로 들어보면,</p>
<blockquote>
<p>소비자가 A라는 상품을 사기 위해서는 Factory 공장에서 일정한 패턴을 통해 찍어내서 상품을 만들어 판매한다. 
이러한 패턴 속에서 소비자가 원하는 원료를 가지고 A상품에 들어가는 원료를 조금 바꿔 B라는 상품을 만들어도 무방하다.
즉, 소비자(client)가 원하는 모델이나 상품을 알맞게 만들어 주는 일정규격이라고 생각하면 된다. </p>
</blockquote>
<p>다시 코드 예제로 돌아오면, 
Movie에도 다양한 형태의 Movie 객체가 있을 수 있다. 파라미터별로 달라지고, 내부 형태도 다를 수 있다. 하지만 이러한 사실은 소비자(client)는 전혀 모른다. 그렇기 때문에 소비자가 입력을 하면 입력값에 맞게끔 원하는 값을 도출해내는 것이 Factory Pattern이라고 보면 된다. </p>
<p>다시 코드를 가져오면,</p>
<pre><code class="language-dart">factory Movie.fromJson(Map&lt;String, dynamic&gt; json) {

    adult: json[&quot;adult&quot;],
    backdropPath: json[&quot;backdrop_path&quot;] ?? &#39;&#39;,
    genreIds: List&lt;int&gt;.from(json[&quot;genre ids&quot;]

    ...
    ...

}</code></pre>
<p>이 코드에서 보면 현재는 Map에 adult, backdropPath, genreIds를 Key로 갖고있다. </p>
<p>그러나 value쪽(특히 backdropPath)을 보게되면 </p>
<pre><code class="language-dart">backdropPath: json[&quot;backdrop_path&quot;] ?? &#39;&#39;,</code></pre>
<p>이러한 모습을 띄고 있는 것을 볼 수 있다. </p>
<p>이게 사용자의 요구에 맞춘건데, 공장(Factory)에는 <code>backdropPath</code>라는 것이 있는데, 이게 사용자가 보내주는 것에 따라 유동적으로 값이 변경될 수 있다는 의미다. </p>
<blockquote>
<p>즉, 사용자가 A라는 상품을 사기 위해서는 공장에 adult, genreIds, backdropPath와 같은 것들이 필요했지만, B라는 상품을 사기 위해서는 공장에 adult, genreIds는 필요하지만 backdropPath는 불필요할 수 있다는 것이다.  -&gt; 필요한 아이템들만 사용</p>
</blockquote>
<p>이렇게 하면 장점</p>
<p>1) 싱글톤 패턴이라해서, static을 사용한 효과를 얻을 수있고,  정적으로 사용할 수 있다. = 객체를 생성하지 않고도 사용할 수 있다. </p>
<p>2) 설계도면이 요구사항에 바뀌어도, 클라이언트는 모델생성에 관여하지 않기 때문에 소스 수정을 팩토리 패턴을 사용한 모델에서만 수정할 수 있다는 장점이 있다. </p>
<br>
<br>

<h2 id="실습">실습</h2>
<br>
<br>


<p>실제 코드 예제 (피자가게에서 피자를 주문하고 그 가격을 출력하는 예제)</p>
<pre><code class="language-dart">void main() {
  var userSelectedPizza = PizzaType.HamMushroom;
  Pizza pizza;

  switch(userSelectedPizza) {
    case PizzaType.HamMushroom:
      pizza = HamAndMushroomPizza();
      break;

     case PizzaType.Deluxe:
      pizza = DeluxePizza();
      break;

     case PizzaType.Seafood:
      pizza = SeafoodPizza();
      break;
  }
  print(pizza.getPrice()); // 출력 : 10.5

}

enum PizzaType { HamMushroom, Deluxe, Seafood}

abstract class Pizza{ // 추상클래스 
  double getPrice(); 
}

class HamAndMushroomPizza implements Pizza {
  double price = 10.5;

  @override
  double getPrice() {
    return price;
  }
}

class DeluxePizza implements Pizza {  
  double price = 7.5;

  @override
  double getPrice() {
    return price; 
  }
}

class SeafoodPizza implements Pizza {
  double price = 12.5;

  @override
  double getPrice() {
    return price;
  }
}

</code></pre>
<p>위 코드는 팩토리 메서드를 사용하지 않은 코드이다. 일반적인 코드로, 사용자의 입력값에 맞게 해당하는 클래스 및 메서드가 할당 및 실행되는 것을 볼 수 있다. </p>
<p>아래 코드는 팩토리 메서드를 사용한 코드이다. </p>
<pre><code class="language-dart">void main() {
 Map&lt;String, dynamic&gt; json = {
   &quot;type&quot;:PizzaType.Pineapple,
 };
 print(Pizza.pizzaFactory(PizzaType.Pineapple).getPrice());
}

...

abstract class Pizza {
  double getPrize();
  factory Pizza.fromJson(Map&lt;String, dynamic&gt; json) {
    switch(json[&quot;type&quot;] as PizzaType) {
      case PizzaType.Pineapple:
        return PineapplePizza();
      case PizzaType.Mushroom:
        return MushroomPizza();
      case PizzaType.Cheese:
        return CheesePizza();
    }
  }
} 

...
</code></pre>
<p>굳이 팩토리 패턴을 사용해야 할까? 사실 상관없다.
하지만 클라이언트 부분(위 코드에선 메인함수)에서 참조를 하지 않는다는 점이 있다. 팩토리 패턴을 사용하지 않은 상태에서 한 클래스를 수정한다면 참조하는 모든 클래스에서 오류가 발생할 것이다. 엄청 번거로운 작업이다.</p>
<p>하지만 팩토리패턴에서 수정이 이루어진다면 팩토리 패턴을 사용한 곳에서만 오류가 발생할 것이다.</p>
<p>더 저렴한 코드비용으로 큰 기댓값을 불러올 수 있다는 장점이 있꾸나!</p>
<br>
<br>
<br>
<br>
<br>
<br>
<br>


<p>[출처 : 이론편]  <a href="https://www.youtube.com/watch?v=ZikfiBnFMk8">https://www.youtube.com/watch?v=ZikfiBnFMk8</a>
[출처 :  실습편 ]<a href="https://www.youtube.com/watch?v=ICaYCojSPko&amp;t=29s">https://www.youtube.com/watch?v=ICaYCojSPko&amp;t=29s</a>
[출처] <a href="https://velog.io/@gou5met/Flutter-%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%8C%A8%ED%84%B4factory-pattern">https://velog.io/@gou5met/Flutter-%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%8C%A8%ED%84%B4factory-pattern</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[flutter] 플러터에서 json 사용하기 ]]></title>
            <link>https://velog.io/@bxxloob_-/flutter-%ED%94%8C%EB%9F%AC%ED%84%B0%EC%97%90%EC%84%9C-json-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@bxxloob_-/flutter-%ED%94%8C%EB%9F%AC%ED%84%B0%EC%97%90%EC%84%9C-json-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 27 Sep 2023 01:30:07 GMT</pubDate>
            <description><![CDATA[<p>플러터에서 json을 사용하기 위해서는 아래와 같이 4가지의 과정을 이해해야한다.</p>
<br>



<blockquote>
<ol>
<li>자료형에 대한 이해</li>
<li>json 스트링을 Map&lt;String, dynamic&gt; 자료형으로 변환</li>
<li>Map&lt;String, dynamic&gt; 자료형을 객체로 변환</li>
<li>객체를 json 스트링으로 변환.</li>
</ol>
</blockquote>
<h2 id="1-자료형에-대한-이해">1. 자료형에 대한 이해</h2>
<ul>
<li>JSON은 데이터 포맷일 뿐이며, 어떠한 통신 방법도, 프로그래밍 문법도 아닌 단순히 데이터를 표시하는 표현방법임.</li>
<li>JSON 데이터는 텍스트 데이터이다. 텍스트 데이터는 String 형으로 들어오게 됨.</li>
<li>또한 JSON은 Javascript의 객체를 만들 때 사용하는 표현식을 의미하며, Javascript의 객체와 같이 Key-value 형태로 이루어져있다. </li>
</ul>
<pre><code class="language-Javascript">{
  &quot;employees&quot;: [
    {
      &quot;name&quot;: &quot;Surim&quot;,
      &quot;lastName&quot;: &quot;Son&quot;
    },
    {
      &quot;name&quot;: &quot;Someone&quot;,
      &quot;lastName&quot;: &quot;Huh&quot;
    },
    {
      &quot;name&quot;: &quot;Someone else&quot;,
      &quot;lastName&quot;: &quot;Kim&quot;
    } 
  ]
}</code></pre>
<ul>
<li>또한 Flutter의 Map&lt;String, dynamic&gt; 자료형을 알아야한다. 
웬만해서는 텍스트 데이터인 JSON은 key값으로 String 타입의 키 값이 들어온다.</li>
<li>dynamic은 데이터 타입이 정적으로 추론되지 않는 동적 타입을 나타내는 특별한 키워드로, 코드 실행시점에타입이 결정된다.<ul>
<li>즉, 어떠한 종류의 데이터든 저장할 수 있고, 실행 시점에 해당 데이터의 타입을 확인하여 작업을 수행하기 때문에 동적이고 유연한 타입이기에 key-value 형태의 value가 어떠한 값이 들어와도 받을 수 있다. </li>
</ul>
</li>
</ul>
<p>! 이 자료형들로 보게되면, 텍스트 데이터인 JSON을 Map&lt;String, dynamic&gt;을 통해 다루고, 이 다뤄진 데이터를 객체로 만들면 되는 것이다. !</p>
<h2 id="json-값을-mapstring-dynamic-자료형으로-변환">JSON 값을 Map&lt;String, dynamic&gt; 자료형으로 변환</h2>
<pre><code class="language-dart">String jsonString = &#39;&#39;&#39;

{

&quot;name&quot; : &quot;hallur&quot;,

&quot;age&quot; : 27,

&quot;job&quot; : &quot;DJ&quot;,

&quot;is_man&quot; : true

}

&#39;&#39;&#39;;</code></pre>
<p>JSON 데이터를 우선적으로 이렇게 String화 시킨다. 보기에 형태는 Key-value로 이루어져있는 것을 알 수 있다. </p>
<p>이러한 String값을 <em>jsonDecode()</em> 라는 메서드를 통해 Map형태로 파싱한다. </p>
<pre><code class="language-dart">Map&lt;String, dynamic&gt; jsonData = jsonDecode(jsonString);

print(jsonData[&#39;name&#39;]); // hallur 출력

print(jsonData[&#39;age&#39;]); // 27 출력

print(jsonData[&#39;job&#39;]); // DJ 출력

print(jsonData[&#39;is_man&#39;]); // true 출력</code></pre>
<p>jsonDecode를 통해 Map&lt;String, dynamic&gt; 형태로 파싱했다. 실제로 코드를 실행해보면 아래와 위와같은 출력값을 기대할 수 있다. </p>
<br>

<h2 id="mapstring-dynamic-자료형을-객체로-변환">Map&lt;String, dynamic&gt; 자료형을 객체로 변환</h2>
<p>JSON 스트링을 JSON 자료형으로 변환켰으니, 이젠 객체로 변환할 차례이다.</p>
<blockquote>
<p>이 과정을 수행하기 위해선 먼저 json데이터와 매칭되는 객체를 정의해줘야함.</p>
</blockquote>
<pre><code class="language-dart">
class User {
    String name;
    int age;
    String job;
    bool isMan;

    User({
        this.name,
        this.age,
        this.job,
        this.isMan
     });

}
</code></pre>
<p>이렇게 정의하고 나면 클래스 내부의 <a href="https://velog.io/@bxxloob_-/flutter-Factory-Pattern%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%8C%A8%ED%84%B4">팩토리 함수</a>를 이용하여 fromJson이라는 함수를 정의해주어야 합니다. </p>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br><br>
<br>





<p>[출처] : <a href="https://totally-developer.tistory.com/121">https://totally-developer.tistory.com/121</a>
[출처] : <a href="https://velog.io/@surim014/JSON%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80">https://velog.io/@surim014/JSON%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</a>
[출처] : <a href="https://bebesoft.tistory.com/11">https://bebesoft.tistory.com/11</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Flutter] ListView vs ListView.builder]]></title>
            <link>https://velog.io/@bxxloob_-/Flutter-ListView-vs-ListView.builder</link>
            <guid>https://velog.io/@bxxloob_-/Flutter-ListView-vs-ListView.builder</guid>
            <pubDate>Mon, 25 Sep 2023 06:40:42 GMT</pubDate>
            <description><![CDATA[<p>문득 강의를 듣다 ListView와 ListView.builder의 차이점이 궁금해졌다 . </p>
<pre><code class="language-dart">// ListView 방식
return MainLayout(
        title: &#39;ListViewScreen&#39;,
        body: ListView(
          children: numbers
              .map((e) =&gt; renderContainer(
                  color: rainbowColors[e % rainbowColors.length], index: e))
              .toList(),
        ));


VS

// ListView.builder 방식
return MainLayout(
      title: &#39;ListViewScreen&#39;,
      body: ListView.builder(
        itemBuilder: (context, index) {
          return renderContainer(
            color: rainbowColors[index % rainbowColors.length],
            index: index,
          );
        },
      ),
    );</code></pre>
<h2 id="1-listviewbuilder-사용-vs-listview-사용">1. ListView.builder 사용 vs. ListView 사용:</h2>
<br>

<h3 id="첫-번째-코드는-간단한-정적-리스트를-위해-listview를-사용합니다">첫 번째 코드는 간단한 정적 리스트를 위해 ListView를 사용합니다.</h3>
<p>ListView는 리스트의 모든 항목을 미리 지정된 children 속성을 통해 생성합니다. 이 방법은 정적인 내용을 표시하는 데 유용합니다.</p>
<h3 id="두-번째-코드는-listviewbuilder를-사용하여-리스트-뷰를-만듭니다">두 번째 코드는 ListView.builder를 사용하여 리스트 뷰를 만듭니다.</h3>
<p>ListView.builder는 항목을 생성할 때 필요한 내용을 빌드 함수(itemBuilder)를 통해 동적으로 생성합니다. 이것은 대규모 데이터 목록과 함께 사용하기에 효율적입니다.</p>
<br>









<h2 id="2-데이터-소스-차이">2. 데이터 소스 차이</h2>
<br>

<h3 id="첫-번째-코드는-기본적으로-비슷한-결과를-생성하며-rendercontainer-함수를-사용하여-각-항목을-표시합니다">첫 번째 코드는 기본적으로 비슷한 결과를 생성하며, renderContainer 함수를 사용하여 각 항목을 표시합니다.</h3>
<p>선택은 사용 사례 및 데이터의 특성에 따라 달라질 수 있으며, 동적인 리스트를 만들어야 하는 경우 ListView.builder가 더 효율적일 수 있습니다. 반면에 정적인 리스트를 만들어야 하는 경우 ListView를 사용하는 것이 간단하고 효과적일 수 있습니다.</p>
<br>

<h3 id="두-번째-코드는-listviewbuilder를-사용하며-rendercontainer를-데이터와-함께-반복-호출하여-각-항목을-렌더링합니다">두 번째 코드는 ListView.builder를 사용하며, renderContainer를 데이터와 함께 반복 호출하여 각 항목을 렌더링합니다.</h3>
<p>데이터의 크기나 개수에 따라 동적으로 항목을 생성할 수 있으므로 대규모 목록에 적합합니다.
두 번째 코드는 ListView를 사용하며, 데이터 목록(numbers)의 각 항목을 루프를 통해 renderContainer에 전달하여 정적인 리스트를 만듭니다.</p>
<br>


<h2 id="3-렌더링-방식">3. 렌더링 방식</h2>
<h3 id="첫-번째-코드는-singlechildscrollview처럼-모든-리소스를-한번에-빌드하고-렌더링-해놓는다">첫 번째 코드는 SingleChildScrollView처럼 모든 리소스를 한번에 빌드하고 렌더링 해놓는다.</h3>
<p>디스플레이나 화면에 보이지 않더라도 해당하는 모든 리소스들을 한번에 렌더링합니다.</p>
<h3 id="두-번째-코드는-builder를-사용하기-때문에-렌더링되는-양을-화면에-보여지는것만-렌더링한다">두 번째 코드는 builder를 사용하기 때문에, 렌더링되는 양을 화면에 보여지는것만 렌더링한다.</h3>
<p>무슨 말이냐, 어떠한 크기로 한 화면에 컨테이너가 3개밖에 보이지 않는다면 보이는 3개 앞뒤로 조금씩만 렌더링한다 . 또한 아래로 내려갈때도 올라갈때도 미리 렌더링 했었던 위젯들을 다시 렌더링하는 것을 볼 수 있는데, 이는 화면밖으로 벗어나면 위젯을 삭제하고 메모리에서 삭제해버린다는 의미를 갖습니다. 
그렇기 때문에 속도 측면이나 메모리 측면에서도 유리하다고 볼 수 있습니다 . </p>
<h3 id="이렇게하면-첫-번째-코드는-퍼포먼스를-저하시킬-수-있고--두-번째-코드는-적재적소에-렌더링하기-떄문에-성능정하나-퍼포먼스-성능에서-유리한-점이-있다-">이렇게하면 첫 번째 코드는 퍼포먼스를 저하시킬 수 있고,  두 번째 코드는 적재적소에 렌더링하기 떄문에 성능정하나 퍼포먼스 성능에서 유리한 점이 있다 .</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[[플러터] 기본 생성자 생성방식 수정]]></title>
            <link>https://velog.io/@bxxloob_-/%ED%94%8C%EB%9F%AC%ED%84%B0-%EA%B8%B0%EB%B3%B8-%EC%83%9D%EC%84%B1%EC%9E%90-%EC%83%9D%EC%84%B1%EB%B0%A9%EC%8B%9D-%EC%88%98%EC%A0%95</link>
            <guid>https://velog.io/@bxxloob_-/%ED%94%8C%EB%9F%AC%ED%84%B0-%EA%B8%B0%EB%B3%B8-%EC%83%9D%EC%84%B1%EC%9E%90-%EC%83%9D%EC%84%B1%EB%B0%A9%EC%8B%9D-%EC%88%98%EC%A0%95</guid>
            <pubDate>Tue, 12 Sep 2023 02:30:42 GMT</pubDate>
            <description><![CDATA[<h2 id="기본방식-수정">기본방식 수정</h2>
<p>상단 메뉴에서 &quot;File&quot; 또는 &quot;파일&quot; (macOS)을 클릭하고, &quot;Settings&quot; 또는 &quot;환경 설정&quot; (macOS)을 선택합니다.</p>
<br>
왼쪽 패널에서 "Editor"를 확장하고, "Live Templates"를 선택합니다.

<br>
"Dart" 항목을 확장하고 "stless" 템플릿을 찾습니다. 만약 없다면 "stless" 템플릿을 생성할 수 있습니다. 

<br>
"stless" 템플릿을 선택하고 편집 아이콘 (연필 아이콘)을 클릭하여 수정합니다.

<br>
"Code" 탭을 선택하고, 템플릿의 내용을 다음과 같이 수정합니다:

<br>

<pre><code class="language-dart">class $NAME$ extends StatelessWidget {
  const $NAME$({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      child: $END$,
    );
  }
}</code></pre>
<br>

<p>이제 Android Studio에서 stless를 입력하고 Enter 키를 누르면 수정한 형식으로 StatelessWidget 템플릿이 생성됩니다.</p>
<br>

<h2 id="새로운-방식-추가">새로운 방식 추가</h2>
<p>&quot;Live Templates&quot; 설정에서 &quot;Dart&quot; 항목을 확장하고, &quot;stless&quot;와 같은 사용할 이름을 가진 새로운 템플릿을 선택합니다.</p>
<p>템플릿 내용을 위와 같이 수정하고 저장합니다.</p>
<p>이제 Android Studio에서 stless를 입력하고 Enter 키를 누르면 새로운 템플릿이 사용됩니다.</p>
<p>템플릿을 수정하거나 추가한 후에도 여전히 원하는대로 작동하지 않는 경우, Android Studio를 재시작하고 캐시를 지울 수도 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Flutter] StatefulWidget과 State 클래스 ]]></title>
            <link>https://velog.io/@bxxloob_-/Flutter-StatefulWidget%EA%B3%BC-State-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@bxxloob_-/Flutter-StatefulWidget%EA%B3%BC-State-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Mon, 04 Sep 2023 06:09:13 GMT</pubDate>
            <description><![CDATA[<h2 id="statefulwidget-클래스">StatefulWidget 클래스</h2>
<ul>
<li>StatefulWidget 클래스는 위젯의 상태를 관리하고 상태가 변경될 때 알립니다.</li>
<li>StatefulWidget은 렌더링되는 부분과 상태를 분리하여 상태 관리를 가능하게 합니다.</li>
<li>주로 StatelessWidget과 함께 사용되며, StatelessWidget은 렌더링된 UI를 정의하고 StatefulWidget은 해당 UI의 상태를 관리합니다.</li>
</ul>
<pre><code>class CounterApp extends StatefulWidget {
  @override
  _CounterAppState createState() =&gt; _CounterAppState();
}</code></pre><h2 id="state-클래스">State 클래스</h2>
<ul>
<li>State 클래스는 StatefulWidget에 연결되어 해당 위젯의 상태를 저장하고 관리합니다.</li>
<li>State 객체는 렌더링된 UI의 상태를 변경하고 이를 화면에 반영하는 역할을 합니다.</li>
<li>State 객체는 build 메서드를 구현하여 화면을 정의하고 반환합니다.</li>
</ul>
<pre><code>class _CounterAppState extends State&lt;CounterApp&gt; {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(&#39;Counter App&#39;),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: &lt;Widget&gt;[
            Text(
              &#39;$_counter&#39;,
              style: TextStyle(fontSize: 48.0),
            ),
            RaisedButton(
              onPressed: _incrementCounter,
              child: Text(&#39;Increment&#39;),
            ),
          ],
        ),
      ),
    );
  }
}</code></pre><p>위의 예시에서 CounterApp 클래스는 StatefulWidget로 정의되고, _CounterAppState 클래스는 이에 연결된 State 클래스입니다. _CounterAppState 클래스의 build 메서드는 UI를 정의하며, _counter 변수의 값을 변경하는 _incrementCounter 메서드를 호출할 때 setState 함수를 사용하여 UI 갱신을 요청합니다.</p>
<p>이렇게 StatefulWidget과 State 클래스를 사용하여 위젯의 상태 관리와 UI 표현을 분리함으로써, Flutter 앱은 상태 변경에 따라 동적으로 화면을 업데이트하고 사용자 상호작용을 처리할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Flutter] StatefulWidget ]]></title>
            <link>https://velog.io/@bxxloob_-/Flutter-StatefulWidget</link>
            <guid>https://velog.io/@bxxloob_-/Flutter-StatefulWidget</guid>
            <pubDate>Fri, 01 Sep 2023 01:28:23 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 포스팅은 코드팩토리님의 인프런 강의를 토대로 작성되었습니다. 
<a href="https://www.inflearn.com/course/%ED%94%8C%EB%9F%AC%ED%84%B0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8/dashboard">https://www.inflearn.com/course/%ED%94%8C%EB%9F%AC%ED%84%B0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8/dashboard</a></p>
</blockquote>
<h2 id="widget-이론">Widget 이론</h2>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/3cab43b6-a22b-4ffd-ba7c-01b49e1a25ea/image.png" alt=""></p>
<br>

<ul>
<li><strong>모든 위젯에 해당하는 내용</strong></li>
<li>위젯은 모두 _<strong>불변</strong>_의 법칙을 갖고있다.</li>
<li>하지만 위젯의 값을 변경해야 할 때가 있다. (색 변경 등)</li>
<li>변경이 필요하면 기존 위젯을 삭제해버리고 완전 새로운 위젯으로 대체한다. </li>
</ul>
<p>위 그림을 보면, 빨간 컨테이너가 파란 컨테이너로 변한 것 같지만, 실제로는 아예 다른 컨테이너이다. 기존의 컨테이너(위젯)을 삭제하고 새로운 컨테이너(위젯)을 만들어 사용하는 것이다. </p>
<br>




<h2 id="statelesswidget-라이프-사이클">StatelessWidget 라이프 사이클</h2>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/d8d2ab75-ffcb-4167-8085-f3321f17efef/image.png" alt=""></p>
<ul>
<li>라이프 사이클은 생명주기이다. </li>
<li>사진 순서대로 생성자로 생성이 되고, 생성이 되자마자 build함수가 실행된다. </li>
<li>이전 Container 예제와 마찬가지로 변경이 필요하면 새로운 위젯을 만들어버린다. </li>
<li><em>** 하나의 StatelessWidget은 라이프 사이클동안 단 한번만 build 함수를 실행한다.**</em></li>
</ul>
<p>따라서, 빌드함수는 절대적으로 단 한번만 이루어지고, 새로운 위젯을 만들면 또 빌드함수를 실행하는 것이 아니라, 새로운 위젯의 새로운 빌드함수가 실행되어 아예 새로운 위젯이 되는 것이다. </p>
</br>


<h2 id="statefulwidget-라이프-사이클">StatefulWidget 라이프 사이클</h2>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/ac83da65-0046-4c48-86ca-1b9b579704e4/image.png" alt=""></p>
<p>그림을 보면, StatefulWidget은 StatefulWidget 클래스와 State클래스로 구성된다. </p>
<blockquote>
<p>Stateful이란 상태를 관리할 수 있다는 말이다. 그러나, 상태를 관리할 수 있다는 말은, build함수를 여러번 실행한다는 말인데, 위젯의 불변의 법칙을 가지고 있다. 그렇다면 어떻게 상태를 관리한다는 말인가?</p>
</blockquote>
<ul>
<li>불변의 법칙을 거스를 수 없기 때문에, StatefulWidget하고 State , 두 Class로 나눠서 구성하는 것이다. </li>
</ul>
</br>



<h3 id="기본-stateful-생명주기">기본 Stateful 생명주기</h3>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/1ffc8e37-9398-49ba-afe7-804ff0457ddd/image.png" alt=""></p>
<ul>
<li>우선 클래스의 생성자가 실행된다. 그 후에는 Stateless에선 원래 build함수가 불렸는데,  createState라는 함수가 실행된다. </li>
<li>State에서 constructor는 중요하지 않다. </li>
<li>initState또한 State가 생성될 때 build함수처럼 절대적으로 단 한번만 실행된다.</li>
</ul>
</br>


<h3 id="파라미터가-바뀌었을때-stateful-생명주기">파라미터가 바뀌었을때 Stateful 생명주기</h3>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/297b8173-dcd8-49e0-9c11-bcc39e5522f6/image.png" alt=""></p>
<p><code>예를들면, 빨간색 컨테이너가 파란색 컨테이너로 변경됐을 때, 그런 상태의 생명주기</code></p>
<p>Stateless는 빨간색 컨테이너를 삭제하고, 파란색 컨테이너를 새롭게 빌드하는것
Stateful은 마찬가지로 기존 위젯은 삭제되고, 새로운 위젯의 constructor가 실행되지만 createState는 실행되지 않는다. (2번 과정 참고)
생성자가 실행되면, 기존에 빨간색 컨테이너가 갖고있던 State에 그대로 붙어버린다. 
원래 있던 State를 재활용하게 된다. 
그 다음엔 4,5,6 과정을 순서대로 진행하며 build를 진행한다. </p>
<blockquote>
<p>정리하면, 파라미터가 바뀌어도 기존 위젯을 삭제되고 새로 위젯이 constructor에 생성되지만, 이 새롭게 생성된 위젯은 기존에 사용중이던 State에 재활용하게 된다. 그리고 생성자를 통해 새로운 파라미터가 들어왔으니 3번 과정인 didUpdateWidget을 실행하고 , 그 후엔 dirty상태가 되고 이를 빌드하여 clean되는 과정이 된다. </p>
</blockquote>
</br>

<h3 id="setstate를-실행했을-때-생명주기">setState를 실행했을 때 생명주기</h3>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/2107cc22-995f-443d-be8c-00bd19fefa2e/image.png" alt=""></p>
<ul>
<li>setState란? StatefulWidget을 생성하면 특별하게 실행할 수 있는 함수가 있다. 그게 setState이다. 이는 State 클래스 내부에서 직접 실행할 수 있다. 보통은 인스턴스를 만들면서 파라미터로 외부에서 참조하게끔 했는데, 이는 내부에서 직접 참조할 수 있게 된다. </li>
</ul>
<p>이 기능 때문에 Stateful을 많이 사용하게 된다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[flutter] flutter/dart 변수타입 정리 ]]></title>
            <link>https://velog.io/@bxxloob_-/flutter-dynamic-vs-var</link>
            <guid>https://velog.io/@bxxloob_-/flutter-dynamic-vs-var</guid>
            <pubDate>Thu, 31 Aug 2023 00:58:13 GMT</pubDate>
            <description><![CDATA[<h2 id="기본타입">기본타입</h2>
<p>int
bool
double
String
... </p>
<h2 id="var">var</h2>
<br>

<p>var타입은 우리가 선언할 때 그 타입으로  fix된다. 즉, 데이터 타입이 선언 당시에 결정되고, 이는 변경이 불가하다. </p>
<p>ex) var v1 = &#39;레드벨벳&#39;
v1 = 5 ----&gt; 에러</p>
<pre><code>에러코드
A value of type &#39;int&#39; can&#39;t be assigned to variable of type &#39;String&#39;.
Try changing the type of the variable or casting the right-hand type to &#39;String&#39;.

&#39;Int&#39; 형식의 값은 &#39;String&#39; 형식의 변수에 할당할 수 없습니다.
변수 유형을 변경하거나 오른쪽 유형을 &#39;String&#39;으로 캐스팅합니다.</code></pre><p>var v1 = &#39;레드벨벳&#39; 이라고 선언하는 당시, 변수 v1의 데이터 타입은 String으로 고정되고, 고정된 이후에는 String 값 이외의 유형을 캐스팅하거나 값을 넣을 수 없음. </p>
<br>

<h2 id="dynamic">dynamic</h2>
<p>이와 달리 dynamic은 선언 당시의 데이터 타입이 고정적이지 않고 유동적이다. </p>
<pre><code> dynamic d1 = &#39;레드벨벳&#39;;
 ...
 d1 = 5;

 에러 x</code></pre> </br>



<h2 id="nullable--non-nullable">nullable / non-nullable</h2>
<blockquote>
<p>1.nullable - null이 될 수 있다
2.  non-nullable - null이 될 수 없다
3.null - 아무런 값도 있지 않다</p>
</blockquote>
<pre><code>  ex)
  String name = &#39;코드팩토리&#39;;


  print(name);
//name = null;

  // 어떠한 데이터 타입 뒤에도 &#39;?&#39; (물음표)만 들어가면 nullable, null이 될 수도 있는 상태를 의미하는 것이다 .
  // 변수타입에 ?가 들어가있는 변수명 뒤에 느낌표 ! 가 들어가면, 절대로 null 이 아니다 라는 것을 의미하는 것이다 .
  // 예를 들어  String? hello = &#39;world&#39;;이고
  // print(hello!); 이면
  // hello는 절대로 null이 아니다라는 의미이다. 즉 ?가 선행되어야 하고, 그 다음 ?가 붙어있는 변수 뒤에 !가 따라오는 것이다 .

  String? name2 = &#39;블랙핑크&#39;;
  print(name2);

  String name3 = &#39;하이&#39;;
  print(name3);</code></pre><p>  하나 더 null관련</p>
<pre><code>  double? number = 4.0;              1)
  print(number); --&gt; 4.0

  number = 2.0;                      2)
  print(number); --&gt; 2.0

  number = null;                      3)
 print(number); --&gt; null;


 number ??= 3.0;                     4)
 print(number); --&gt; 3.0

 여기서 ??= 라는 연산자는 좌측(변수)에 있는 값이 null이면? 오른쪽 값을 넣어서 대입해라. 
 즉, 3번에서  number에 null값을 넣었고, 출력도 확인했다. 현재 상태는 null값이니까 number ??= 3.0 이라고 하면 3.0으로 값이 대입되기에 출력시 3.0이 나오는 것이다. 

</code></pre><h2 id="final--const">final / const</h2>
<p>  final a = &#39;코드팩토리&#39;;
  const b = &#39;블랙핑크&#39;;</p>
<p>const는 컴파일 때 변수가 초기화. final은 런타임때 변수가 초기화. </p>
<pre><code>final DateTime now = DateTime.now();  -&gt; 1)
const DateTime now2 = DateTime.now(); -&gt; 2)</code></pre><blockquote>
<p>1)은 정상. DateTime 자체가 런타임때 값을 저장하는 변수이고, final 또한 런타임때 초기화 하기 때문에 사용해도 상관x. 
그러나 <strong>2)는 에러.</strong> 왜냐하면 const는 런타임이 아닌 컴파일 때 값을 초기화하기 때문에 각각이 변수를 초기화 하는 시점이 달라 에러발생. </p>
</blockquote>
<h2 id="list-map-set">List, Map, Set</h2>
<pre><code>// List
  // 리스트

  List&lt;String&gt; blackPink = [&#39;제니&#39;, &#39;지수&#39;, &#39;로제&#39;, &#39;리사&#39;];
  List&lt;int&gt; numbers = [1,2,3,4,5,6];
  print(blackPink.length);
  print(numbers);

  blackPink.add(&#39;코드팩토리&#39;);
  print(blackPink);

  blackPink.remove(&#39;코드팩토리&#39;);
  print(blackPink);

  print(blackPink.indexOf(&#39;로제&#39;));

  // Map
  // Key / Value
  Map&lt;String, String&gt; dictionary = {
    &#39;Harry Porter&#39;:&#39;해리포터&#39;,
    &#39;Ron Weasley&#39; : &#39;론 위즐리&#39;,
    &#39;Hermione Granger&#39; : &#39;헤르미온느 그레인져&#39;,
  }; 
  print(dictionary);

 Map&lt;String, bool&gt; isHarryPorter = {
   &#39;Harry Porter&#39;: true,
    &#39;Ron Weasley&#39; : true,
    &#39;Hermione Granger&#39; : true,
   &#39;Iron Man&#39; : false,
 };

  print(isHarryPorter);
  isHarryPorter.addAll({
    &#39;SpdierMan&#39; : false,
  });
  print(isHarryPorter);


  print(isHarryPorter);
  print(isHarryPorter[&#39;Iron Man&#39;]);

  isHarryPorter[&#39;Hulk&#39;] = false;
  print(isHarryPorter);

  isHarryPorter[&#39;Hulk&#39;] = true;
  print(isHarryPorter);

  print(isHarryPorter.keys);

  isHarryPorter.remove(&#39;Harry Porter&#39;);
  print(isHarryPorter); 

  print(isHarryPorter.keys);

  // Set
  final Set&lt;String&gt; names = {
    &#39;Code Factory&#39;,
    &#39;Flutter&#39;, 
    &#39;Black Pink&#39;,
    &#39;Flutter&#39;,
  };

  print(names);

  names.add(&#39;Jenny&#39;);

  print(names);

  names.remove(&#39;Jenny&#39;);
  print(names);

  print(names.contains(&#39;Jenny&#39;));
  print(names.contains(&#39;Flutter&#39;));</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Servlet] 서블릿]]></title>
            <link>https://velog.io/@bxxloob_-/Servlet-%EC%84%9C%EB%B8%94%EB%A6%BF</link>
            <guid>https://velog.io/@bxxloob_-/Servlet-%EC%84%9C%EB%B8%94%EB%A6%BF</guid>
            <pubDate>Sat, 12 Aug 2023 06:29:18 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 포스팅은 김영한님의 &#39;스프링MVC1편 - 백엔드 웹 개발 핵심 기술&#39; 강의를 토대로 작성되었습니다.
[강의출처] : <a href="https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard">https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard</a></p>
</blockquote>
<br>



<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/25837a08-d0b5-41b2-abb8-90337277697f/image.png" alt=""></p>
<p>위와 같은 상황을 가정하자.
이름이랑 나이라는 입력값을 넣어서 전송하면 회원가입이 되는 상황.</p>
<p>여기서 전송 버튼을 누르면 웹 브라우저가 생성한 <code>**요청**</code> HTTP메시지를 만든다.
여기에는 필요한 정보들이 담김.(누가 보냈고, 어디로 보냈고, 어떤걸 보냈고, 어떤 방식으로 보내는 등등)</p>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/7ee49ed8-bd62-4441-a8d0-b3ea01d1bd9b/image.png" alt=""></p>
<p>위에서 보냈던 요청 메세지를 처리하기 위해서는 메세지를 분석해야한다. 또한 비즈니스 로직은 WAS가 메인으로 도맡아 진행하는데, 만약 WAS가 없다면 왼쪽 그림과 같은 로직을 처음부터 다 진행해야한다.</p>
<p>이럴때 필요한 것이 <code>Servlet, 서블릿</code>이다.
서블릿은 들어온 HTTP 요청 메세지를 알아서 파싱해주는 역할을 한다. </p>
<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/bbd058bb-e747-47d5-a2a3-9867147e7627/image.png" alt=""></p>
<p>여기서 <code>service</code>라는 메서드를 보면, request와 response를 파라미터로 받는데, requset가 의미하는 바가 클라이언트(웹브라우저)에서 보는 요청 HTTP이다. 그리고 extends를 통해 HttpServlet을 상속받았기에 알아서 요청 메세지를 파싱해주고, 이에 따른 필요한 애플리케이션 로직 부분만을 가져올 수 있게 된다. </p>
<p>또한 요청에 해당하는 응답을 하기 위해선 파라미터로 받은 response가 필요하고, 이에 대한 응답 메세지 파싱 또한 서블릿이 해주기에 매우 편리하다.</p>
<ul>
<li>urlPatterns(/hello)의 URL이 호출되면 서블릿 코드가 실행</li>
<li>HTTP 요청 정보를 편리하게 사용할 수 있는 HttpServletRequest</li>
<li>HTTP 응답 정보를 편리하게 제공할 수 있는 HttpServletResponse</li>
<li>개발자는 HTTP 스펙을 매우 편리하게 사용</li>
</ul>
<blockquote>
<p>물론 기본적이고 기초적인 HTTP 스펙을 알아야 하긴 한다. 알아야 인지할 수 있고, 응용할 수 있기 때문!</p>
</blockquote>
<br>

<p><img src="https://velog.velcdn.com/images/bxxloob_-/post/38a3cbe2-f917-4c39-aa4f-d89f6291f66e/image.png" alt=""></p>
<p>앞서 보여준 요청/응답 흐름의 전체적인 그림이다. </p>
<p>사진 이해 : 서블릿 / HTTP 요청, 응답 흐름</p>
<p>HTTP 요청시</p>
<ul>
<li>WAS는 Request, Response 객체를 <strong>새로 만들어서</strong> 서블릿 객체 호출</li>
<li>개발자는 Request 객체에서 HTTP 요청 정보를 편리하게 꺼내서 사용</li>
<li>개발자는 Response 객체에 HTTP 응답 정보를 편리하게 입력</li>
<li>WAS는 Response 객체에 담겨잇는 내용으로 HTTP 응답 정보 생성</li>
</ul>
<p>서블릿 컨테이너란? </p>
<br>

<ol>
<li>톰캣처럼 서블릿을 지원하는 WAS를 서블릿 컨테이너라고 함</li>
<li>서블릿 컨테이너는 서블릿 객체를 생성, 초기화, 호출, 종료하는 생명주기 관리</li>
<li>서블릿 객체는 <strong>싱글톤으로 관리</strong></li>
</ol>
<ul>
<li>고객의 요청이 들어올 때 마다 객체를 생성하는 것은 비효율</li>
<li>최초 로딩 시점에 서블릿 객체를 미리 만들어두고 재활용</li>
<li>모든 고객 요청은 동일한 서블릿 객체 인스턴스에 접근</li>
<li><strong>공용 변수 사용 주의</strong></li>
<li>서블릿 컨테이너 종료시 함께 종료</li>
</ul>
<ol start="4">
<li>JSP도 서블릿으로 변환 되어서 사용</li>
<li>동시 요청을 위한 멀티 쓰레드 처리 지원</li>
</ol>
<br>



<blockquote>
<p>❗ 여기서 주의 ❗
위에서 사진 이해를 돕기 위한 글에서 <code>WAS는 Request, Reponse 객체를 새로 만들어서 서블릿 객체 호출</code> 이라고 적어놨는데, requset와 response객체는 들어오는 것마다, 나가는 것마다 다르기 때문에 새로 생성하는 것은 맞다. 그러나 <code>helloServlet</code>이라는 객체를 새로 만들 필요가 없다. 하나를 만들어두고 로직만 변경해가면서 사용하는 것이다. 그래서 공유 변수 사용을 주의하라는 것이다. </p>
</blockquote>
<p>서블릿을 지원하는 WAS가 가진 <code>동시 요청 멀티 쓰레드 처리 지원</code>이 뭘까? </p>
<p>알아보자</p>
]]></description>
        </item>
    </channel>
</rss>