<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>지수의 로그</title>
    <link>https://w97ww.tistory.com/</link>
    <description>Exponentially Growing Developers</description>
    <language>ko</language>
    <pubDate>Tue, 21 Jul 2026 06:33:07 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>지수쓰</managingEditor>
    <image>
      <title>지수의 로그</title>
      <url>https://tistory1.daumcdn.net/tistory/3834803/attach/85e0a60e22af4ad8be59da98058b28b0</url>
      <link>https://w97ww.tistory.com</link>
    </image>
    <item>
      <title>개발자, 직장인 5년 회고</title>
      <link>https://w97ww.tistory.com/122</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;다듬으려고 초안으로 적어놨던건데 그냥 공개하고 나중에 다시 정리해야겠다. 어짜피 부끄러워하기엔 아무도 안읽을테니 걱정하지말기로오... (주의 : 짱길어요)&lt;br&gt;&lt;br&gt;미루고 미뤄왔던 회고를 해보려고 한다. 기술적인 내용보다는 5년에 걸친 전반적인 인생 회고이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;나는 21년 1월에 첫 회사에 입사해서 사회생활을 시작했고 22년 6월에 두번 째 회사로 이직을 했다. 이제 만 4년차가 조금 넘은 5년차 백엔드 개발자이다. &lt;br&gt;&amp;nbsp;&lt;br&gt;이 글을 기록하는 이유는, 나는 일단 기록을 좋아한다. 내 기억은 한계가 있으므로 어딘가에 기록되어 추후에 나의 그때시절 생각이나 배움을 기록을 통해 기억해내고 싶다. &lt;br&gt;그리고 단순히 기술적 성장을 떠나서, 사회생활을 하면서 해가 지나갈 때 마다 배운 점이나 바뀌는 생각들이 많은데 기록 없이 과거를 돌아봤을 때 생각보다 잊혀지는게 많은 것 같다고 느꼈다. 지금 내가 자연스럽게 하는 일들이 당연한게 아니고 그 당시에 정말 안간힘을 쓰고 익혔던 습득된 지식이었음에도 기억하지 못하는게 아쉬워서 적어두고 아 내가 이랬구나. 이 시기에 나는 이렇게 살아왔구나를 적어보고싶다.&lt;/p&gt;&lt;h1 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;span&gt;요즘 드는 생각들&lt;/span&gt;&lt;/h1&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;책 읽기&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;작년부터 조금씩 책을 읽기 시작했다. 20살 이후로 전공책, 기술 서적을 제외하고는 책을 정말 안읽었는데 이제야 책 읽기의 중요성을 깨달았다.&lt;br&gt;처음엔 뭔가 단순히 언어능력을 키우고 싶어서 책을 읽어볼까 하고 시작했었는데 어쩌다 읽게된 심리학 책이 나에게 평소와는 아예 다른 생각을 하게해줘서 요즘엔 나를 탐구하고 찾기 위해서 책을 읽고 있다.&amp;nbsp;&lt;br&gt;한번도 '나'에 대해서 제대로 궁금해 해본적이 없는 것 같아서, 책을 통해 나는 어떤 유형의 사람이고 내 결핍은 무엇인지 알아가는 시간을 가지고 있다. 그리고 내가 지금까지 살면서 겪은, 겪을 힘듦을 단순히 누군가한테 감정적으로 털어놓으면서 잠시 푸는게 아니고 진짜 해결해나가려면 어떻게 해야하는지를 제대로 알아야할 것 같다는 생각이 들었고, 지금은 책에 그 해답이 있다고 느껴졌다.&amp;nbsp;&lt;br&gt;그래서 요즘엔 다양한 종류의 책, 영화를 통해서 간접적으로 경험해보고있다. 원랜 단순하게 해피엔딩, 상업용 드라마/영화 만 찾아봤었는데 취향이 조금씩 변하는 중이다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;달리고 싶다 vs 멈추고 싶다&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;문득 생각해보니 나는 항상 뭔가 단기적인 목표를 향해 달리기만 했던 것 같다. 대입, 학교 시험, 전공학점, 취업, 이직, 프로젝트.. 아마 이대로 가다간 다음 목표는 이직이나 결혼이지 않았을까 싶다&lt;br&gt;대학교 때 1년 휴학을 했지만 그때도 알바, 여행, 공부만 쉬지않고 했다. 휴학하면서 게임 엄청 하고 쉬었다고 생각했는데, 롤 티어 골드 찍어보겠다고 그것 조차 숙제처럼 수련하듯이 한 것 같다. 이제와보니 지금까지 한번도 제대로 멈춰본 적이 없는 것 같다.&amp;nbsp;&lt;br&gt;내 좌우명은 '우물안 개구리가 되지 말자'였다. 제주도를 떠나 육지로 올라올 때도 그렇고, 현실에 안주하지 않고 항상 우물 밖으로 나가려고 노력하는 것이 성공하는 방법이라고 믿었다. 그런데 성공하려면 어쨌든 내가 개구리라는건 깨달아야 우물 밖에 나와도 행복할 수 있을 것 같은데, 나는 내가 누구인지 모르고 산 느낌이다.&amp;nbsp;&lt;br&gt;가만히 좀 멈춰서 지나온 날도 되돌아보고, 내 속도 들여다보고, 나는 어떤 사람인지 알아보고, 표면적으로 드러나는 성취가 아닌 내 스스로의 만족감을 키우는 방법을 찾고싶다.&lt;br&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;뭔가를 할 때 '열심히, 잘, 최선을 다해서 해야겠다'라는 생각을 계속 했는데 당분간은 열심히 하고 싶지도 않고 그냥 멈춰있고싶다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;그러고 나서 달리면 뭐 조금 늦을 수도 있고 뒤쳐지기야 하겠지만 앞에서 1등 꽁무니만 따라가며 벅차하는 것 보단 조금 뒤에서 행복한게 더 나은 것 같다는 생각이 처음으로 들었다.&lt;br&gt;&lt;br&gt;개발자의 기술력이 해마다 꾸준히 늘어가는게 아니라, 주니어시절 습득한 내용이 거의 끝까지 간다는 말을 봤다. 아직 모르는 기술이 많아서 빨리 공부해야할 것 같고 조급한 마음이 드는데, 지금은 기술적 성장보다 나 자신을 알고 내면을 다지는게 먼저라는 생각이 들었다. 기술 스터디는 회사 일과시간에 틈틈이 하는 정도로 일단 만족해야겠다.&amp;nbsp;&amp;nbsp;&lt;br&gt;&lt;br&gt;-&amp;gt; 멘토링한내용 추가하기&lt;br&gt;그래도 지금 공부를 하면 좋을거같긴한데, 목표를 장기적ㅇ로 세워서 천천히하기. 70점만하기&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;취미 생활&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;입사하고 매년 취미가 바뀌었다. 나는 하고싶은게 아직 많나보다. &lt;br&gt;21년,22년엔 롤을 정말 많이 했다. &lt;br&gt;23년엔 운동을 시작했다. 1년 내내 하루에 최소 2시간씩 헬스장에서 살았다. PT도 40회정도 받았다. 근육량도 2키로정도 늘고 만성으로 달고있는 허리디스크도 코어힘이 생기니 통증이 줄었다. (나아졌다가 운동하다 삐끗해서 다시 나빠지기도 했지만..)&lt;br&gt;24년엔 드럼을&amp;nbsp;시작했다. &lt;br&gt;지금까지의 취미는 일이 아닌 다른 눈에 보이는 성취를 만들고싶어서 더 빠져있었던 것 같다. 작지만 하나씩 해내는데서 내 자존감이 올라가고 만족이 생겨서 좋았다. &lt;br&gt;항상 뭔가 해내고, 결과물이있는 취미생활만을 가졌었는데, 최근에 생긴 취미들은 좀 달라졌다. &lt;br&gt;책읽기, 영화보기, 일기쓰기, 혼자 집에서 쉬기, 산책하기, 혼자돌아다니기, 집 청소하고 가꾸기&lt;br&gt;예전같았으면 시간아깝고 재미없다고 했던 것들을 시간내서 하고있다. &lt;br&gt;그리고 이것저것 기록하려고 아이패드도 샀는데 아주 잘 쓰고있다. &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;관계&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;워낙에 애정결핍이 있어서 사람찾아서 항상 누군가와 함께하려고 했다.&lt;br&gt;회사에서도 그러다보니 좋아하는 사람들이랑 다른 팀이 되면 너무 아쉬워지고 다른 팀이 부럽고 그 사람들이 뭐하는지 다 알고싶고.. 그랬다&lt;br&gt;이제는 나 스스로만으로도 채워지는 마음들이 있어서 그렇게 다른 사람들로부터 채워지지 않아도 혼자서도 찾고 행복할 수 있게 됐다. &lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;br&gt;불안 &amp;amp; 정보의 과잉&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;나는 불안함이 많다. 애착유형검사도 해보고 나는 어떤사람인가 돌아보니 나는나는 사람들과의 관계에서도, 일을 할 때도 엄청 불안해하고 완벽주의 성향도 엄청 강하다&lt;br&gt;자꾸 불안해서 더 완벽해지려고 일하다보니 번아웃도 오고, 결정도 빨리 못해서 일할 때 속도도 안났다. 물론 적당한 불안은 좋은점도 많겠지만 너무 &quot;불안해만 하는 것&quot; 은 별로 안좋은 것 같다. 걱정이나 불안을 조금 제쳐두고 실패해도 괜찮으니 일단 해보자는 마음으로 살고 더 경력이 쌓이고 나이들기전에 좀 빠르게,여러번 실패하는 경험을 많이 해봐야할 것 같다.&lt;br&gt;&lt;br&gt;가끔은 요즘 인터넷이나 매체로 내가 알게되는 정보가 너무 많아서 내가 더 불안해지는 것도 같다. 많이 알면 다 도움이 될 것 같았는데, 내가 소화할 수 없는 양의 정보가 들어와서 나를 불안하게 만들고 오히려 불행해지는 것 같아서 어느정도는 정보를 걸러듣거나 굳이 찾아보려하지 말아야겠다는 생각도 했다.&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;br&gt;개발자로서, 사회인으로서, 앞으로 나는 어떤 사람이 되고싶은가&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;IT회사로 이직하면서 나는 앞으로 매일 기술적으로 뒤쳐지지않고 성장하는 사람이 되어야지 하고 생각했다. 그게 '개발자'라고 생각했다. 그런데 점점 그냥 회사에서 시키는 업무를 하고, 현실에 안주하고, 일보다는 제테크나 개인 취미생홀에 관심을 가지니까 나는 '개발자'가 아닌가?하는 생각이 들었다.&lt;br&gt;그런데 업무에 적응하고 회사가 시키는 일을 하는 것도 충분히 개발자 인 것 같다. 소프트웨어엔지니어라고 생각하면 어쨌든 .. 그리고 기술적인 스킬도 중요하지만 소프트스킬도 중요하고, 사람들과 어떻게 업무를 해내는가. 일정을 어떻게 관리하는가. 리소스를 어떻게 관리하는가. 어떻게 기록하는가. 이런것들도 다 중요한 일인 것 같다. 그래서 너무 내가 기술적으로 뒤쳐지지 않을가?하는 걱정만 하면서 불안해하지는 않기로 했다.&lt;br&gt;물론 이직할때가 되면 면접도 봐야하고 코딩테스트도 풀어야하니 기술적으로 연차에 맞게 잘 알고 있어야 하겠지만, 생각보다 나도 모르게 습득하고 있는 지식들이 많을테고 그걸 잘 기록해놓고 내꺼로 만들어두면 추가로 엄청 공부하지 않더라도 많이 성장하고 있을거라고 새각한다.&lt;br&gt;그리고 어느순간이 되면 나는 그냥 사회에서 직장인인걸 떠나서 결혼도 하고 엄마도 되어야하는데, 그때의 나는 '개발자'라는 타이틀에만 몰두할 순 없을거라고 생각한다. 지금도 나를 찾느라고 회사나 일보단 책읽는게 더 하고싶은 상태니깐..&lt;br&gt;앞으로도 계속 나는 어떤 사람이 되고싶은가를 생각하면서 살아야할 것 같다. &lt;br&gt;그리고 1,2년전에 쓴 내 회고글을 보면 &lt;br&gt;항상 나한테 질문이 들어오면 &quot;어,.. 아마 그럴거예요&quot;라고 대답해서 그분들이 결국 더 잘아는사람한테 한번 더 확인을 하느라고 시간을 2번쓰게 만들었고, 그러다보니 나한테 질문을 점점 안하게 됐다. 그게 너무 자괴감이 들고 슬펐다. 그래서 내년에는 '아는것'과 '모르는것'을 잘 구분해서 '아는것'에 대해서는 확신을 가지고 말하기. 라고 목표에 적어놨었다. &lt;br&gt;24년에는 너무 아는척해서 문제일때도 있긴 했지만 자연스럽게 나도 확신을 가지고 말하고 있었다. 그러다가 프로젝트 리딩도 하게 됐다.&lt;br&gt;그리고 어느순간 팔로워 역할만 하다보니, '욕심'이나 '발전'에 대한 의지가 좀 없어지던 순간이 있었다. 내가 주니어든, 처음 하는 업무여서 서툴든 나만의 방식으로 나는 이정도로 해냈다. 나에게 이런 기준이 있었는데 스스로 이만큼 해냈기 때문에 올해는 이것을 기대한다.라구ㅗ 주어진어게 그냥 만족하는 것 보다 내 목소리를 낼 줄아는 사람이 되어보고싶다고 했었는데, 작년에는 그렇게 해봐서 좋았다. &lt;br&gt;이건 개인적인 일이지만 23년 말에 '내 취향을 가지고 나는 어떤것을 좋아하는지 말할 수 있는 사람이 되고싶다'라고 막연하게 생각한 뒤 작년에 내 취향이나 나를 찾는 경험을 많이했고, 지금 되게 나만의 주관을 꽤 가지게 되기도 했다.&lt;br&gt;해마다 작년을 돌아보면서 내년에는 이렇게 해야겠다. 하는 마음을 가지면 나도모르게 의식해서 그런 사람이 되어가고 있고 어느날 보면 자연스럽게 그런 사람이 되어있다. 그래서 앞으로도 그래야겠다고 생각했다.&lt;br&gt;작년에 내가 결심한 것은 기분이 태도가 되지 않기. 우선순위 관리 잘 하기. 일단 짧게짧게 시도하고 결과 내보기. 같은 것들이 있다. &lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;이직하고 나서 느낀점, 앞으로 이직에 대한 마음 &lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;그 회사는 정말 나랑 맞지 않았다. 그 이유가 그 팀 분위기 / 직무 / 회사의 성격 뭐 하나가 딱 뭐라고 할 순 없지만 확실히 안맞았다.&lt;br&gt;나는 B2C를 하는 IT 회사에 오고싶었다. &lt;br&gt;회사 사람들으 ㅣ분위기 컬쳐핏 문화 &lt;br&gt;앞으로 이직을 생각하면, 사실 불안한 마음에 기술적으로 뭔가 성장해야할 것 같고 그러려면 너무 안주해서 다 까먹기전에 어디라도 또 떠나야하나 하는 마음도 있는데, 나는 생각해보면 어딘가에서 거기서 내가 할수있는걸 넓게 최대한 경험해보고 떠나오는 것을 좋아하는 것 같다. 그래서 이 회사에서도 지금 직무뿐아니라 이것저것 회사 ??흠&lt;br&gt;그래도 그 히ㅗ사에서 1년을 일하면서 느낀건, 어떤 회사든 비슷한 점들이 있고 그건 고칠 수 없으니 적응해야한다는 부분이 보인다는거였다.&lt;br&gt;사내에서는 망도 좀 구분되어있어서 답답하고, 근태나 뭔가 불편한 프로세스 방식도 많고 조직개펴노 ㅁ낳고 이런걸 그 회사에서도 이미 겪어왔기때문에 그게 그렇게 화가나진 않다&lt;br&gt;그리고 지금 내가 속한 팀이 너무 재밌고 맘에든다면 그건 지금 정말 운이 좋은거니깐 지금 팀에 있을때 그사람들한테 잘하고 지금을 즐겨야겠다는 생각을 한다. 처음부터 좋은 팀에 들어가면 그게 얼마나 귀한지 잘 모를 것 같다. 되게 많은 조직개편으 ㄹ하고 예전회사에서 이직해오면서 그런것에 소중함은 좀 느끼게된다. &lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;면접 참여&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;면접 참여를 해봤따. 내가 뭘 하겠어 라는 생각으로 안했다가, 그냥 한번 해보고싶어서 사람들의 과제도 평가해보고, 서류도 보고, 면접에도 들어갔다. 다들 어떤 기준으로 평가를 하는지를 살펴보고 나는 그 수많은 지원자들중에 어떤 사람들에게 눈이 가는지도 파악을 해봤다. 몇가지 정리했던 생각들은 이렇다.&lt;br&gt;그리고 우리 회사만 그런지는 모르겠는데, 나는 점점 회사들이 아무리 개발회사라고 하더라도 결국 돈받고 일하는사람을뽑는거라고 생각한다. 엄청나게 기술적으로 뛰어난 사람도 좋지만 컬쳐핏도 꽤 중요하다ㅏ. 그리고 어느정도 연차가 쌓이고 나면 기술적으로는 당연히 알것이고, 혹시 안써본 기술이라고 하더라도 그 기술을 처음이지만 바로 익혀 적용하는 법을 어떻게든 알고있을거싱라 생각한다. 그래서 하나를 하더라도 대충하지 않는사람, 뭔가를 깊게 고민해본적이 있는사람, 연차가 높아질수록 좀 더 넓게 볼 줄 아는사람. 이런식으로 좀 두루뭉술한 기준으로 보게되는 것 같다. 그런데 이건 우리 회사여서 그렇고 좀 더 기술적인 성장이 중요하거나 그런 곳들은 다를 것 같긴 하다. &lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;회사에서 알게된 사람들, 시절인연&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;시절인연이라는 말을 알게됐다. &lt;br&gt;동네친구들, 학교친구들, 대학 동기선후배들, 회사 친구들. 그 시절에 내 환경에서 친해져서 잘 어울리는 시절인연들이 있다. 그리고 그사람들이랑은 어느순간 멀어지게 되기도 한다. 항상 맺은 인연들과 깊은 관계를 유지하긴 힘든 것 같다. 모든 사람들은 사람들마다 마음의 방이 있다. 어떤사람들은 원룸이어서 혼자밖에 못들어올수도 있고, 어떤사람들은 아파트여서 몇십명을 담을수도 있다. 나는 마음의 방이 되게 큰 편이긴 한데, 어쨌든 누군가가 들어오면 누군가는 나가야하고 또 친해진 사람이 생기면 서서히 소홀해진 인연들과는 멀어지는게 좀 당연한 것 같다. 그래서 회사에서 처음 새로 알게된 동료들이랑 엄청 친해지고 진짜 그사람들한테 모든 애정을 다 쏟았는데, 결국 어느덧 조금 소홀해지고 있다. 처음엔 조직개편이나 이것저것의 상황으로 친했던 동료들과 멀어지는게 너무슬펐는데, 너무나 당연한 현상이고 그사람들이랑 만약 계속 친하고싶다면 내가 주도적으로 관계를 유지하면 되는 것이다. &lt;br&gt;그런데 그럴때마다 또 고민인건 사회에서 만난 인연들과 어느정도 사적인 관계를 유지해도 되는지 잘 모르겠다. 너무 좋은 사람들이 있지만 회사에서 너무 가까워지는것도 좋지는 않은 것 같아서.. 예전에는 그래도 모두 좋은대로 친해지자였다가, 이제는 너무 가까워지진 않는 태세로 바뀌었다. 만약 그 사람이 정말 좋으면 나중에 팀이 멀어지거나 회사를 그만두게되었을 때 계속 연락을 주고받으면서 유지하게되겠지 ..&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;술&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;나는 술을 죽어라 마시는 편이었는데, 그래ㅓㅅ 회식도 좋아하고 회식하면 술집이 문닫는 2시까지 내리 마시곤 했다. 그러고 다음날 헤롱헤롱 술냄새 풍기면서 회사에 오기도 했다. 그런데 그게 너무 별로인 것 같았다. 그리고 술을 먹고 실수를 하면 잃을게 너무 많아지는 나이와 사회적 위치가 됐다. 그래서 이젠 술이 너무 무섭고 조절해서 마셔야겠다는 두려움이 생기니 별로 끌리지가 않는다. 근데 그러다가도 회먹으면 소주를 맘시고 싶고 양꼬치를 먹는다는 생각만해도 맥주가 땡긴다. 적당히 절제하고 조절하면서 좋아하는 것을 즐기는 법은 너무 어렵다. 이런게 정말 되는 순간에 어른이 되는 것 같다. &lt;/p&gt;&lt;h1 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;span&gt;내가 처음으로 해냈고, 뿌듯했던 것들, 도전한 것들&lt;/span&gt;&lt;/h1&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;PT&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;23년도에는 40회정도 PT를 받았다. 헬스는 처음이었는데 오기가 생겨서 거의 1년동안은 매일 가고 갈때마다 2시간 넘게 운동을 했다. 근육량도 2kg은 넘게 올렸고 제일 열심히할때는 살도 많이 빠졌었다. &lt;br&gt;오늘 했던게 잘 안되도 내일 하고 모레 해보면 어느날 되는 그 성취감도 참 좋았고, 엄청 힘들고 죽을때까지 했던 경험이있어서 10km 마라톤 처음 해보고 진짜 죽을 것 같았지만 PT 데드할때보단 안힘들다. 하면서 뛸수도 이썼다. &lt;br&gt;뭔가를 꾸준히 한다는것이 나를 정말 지탱해주는 힘이 됐다. 매일매일 헬스를 가는 나에 취해서 나라는 사람에 대해서 자존감도 많이 올라갔었다. &lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;여행&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;꾸준히 여행을 좀 갔고, 제일 큰건 호주여행이다. 호주여행 특가가 떠서 48만원에 브리즈번을 6박7일 다녀왔다. &lt;br&gt;해외에 가서 같은 숙소에 쭉 있으면서 마트에서 장을 봐서 요리해먹고, 여유롭게 동네 구경하면서 여행하고 쉬는게 정말 좋았다. 회사에서 일을 안한다는 것때문에 좋은것도 있는 것 같지만..&lt;br&gt;올해는 1박2일로 혼자 국내여행을 좀 다녀와보려고 한다. 그리고 추석연휴쯤 3주정도 휴가를 길게 써서 오스트리아와 포르투갈을 다녀올거다. 가서 좀 혼자 생각도 많이 하고 이것저것 경험해보면서 내년에 30살이 되는데 새로운 준비를 해볼까한다. &lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;드럼&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 취미로 드럼을 치기 시작하고 작년 6월부터해서 벌써 거의 9개월째다. PT이후로 한달에 내는 돈만 20만원이고 유지비가 꽤 많은 취미생활이지만 이것도 나에게 성취감을 많이 준다. 그리고 스트레스가 많이 풀려서 좋다. &lt;br&gt;어떤 악기 하나 다룰줄아는 사람.이라는 허영심으로 유지하고 있는데 드럼을 치면서도 되게 많은 생각의 변화르 ㄹ준다.&lt;br&gt;20만원 들이니까 진짜 열심히해야지. 왕ㄴ벽하게 연습해야지 하고 일주일을 다 바쳐 연습을 하다가도, &lt;br&gt;아 좀 도 ㄴ아까워도 취미생활이고 행복하자고 하ㅡㄴ건데 연습 좀 못할수도 있고 못하면 뭐 어때 직업으로 삼을것도 아닌데.하면서 내가 완벽하지 않아서 나를 자책할 때 마다 나 스스로 위로도 해주고 자기합리화도 한다.&lt;br&gt;그리고 감정적으로 많이 격해져있고 힘들때 드럼이나 운동으로 머리를 비우고 리프레시하는것도 감정조절하는 좋은 장치인 것 같다.&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;콘서트&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;콘서트를 3개를 해봤는데, 10만원이라는 돈이 너무 크다고 생각했는데 되게 큰 경험을 주었다.&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;요리, 인테리어&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;집에서 요리하고 인테리어하는게 참 재밌다. &lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;일기쓰기&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;똑같이 작년 6월부터 일기를 쓰기 시작했다. 작년에는 사건이 있을때마다 날짜에 상관없이 막 써내려갔는데, 특정 감정에 대해서 조절을 잘 못할 때 일기를 썼는데 다음에도 같은 상황에 같은 감정을 느껴서 일기를 쓰고,, 그게 쌓이다보니 다시 읽어봤을때 내가 이부분에서 엄청 약하구나.가 느껴져서 나라는 사람을 잘 알게되었다. 그리고 다음번엔 좀 더 의식해봐야지, ㅏㅎ면서 나아지는 것도 있는 것 같다.&lt;br&gt;그리고 어딘가에 내가 했던 일이나 생각을 털어놓기만해도 뭔가 엄청 뿌듯하고, 나를 챙기는 마음이 든다. 원래는 그래서 친구들 보는 블로그나 인스타에 엄청 모든걸 다 뱉어냈는데, 일기에 내 부끄러운 감정을 다 배설하고 나니 인스타도 강박적으로 하진 않게 되고 일상 블로그에도 적당한 온도로 글을 쓰게 된다. &lt;br&gt;요즘엔 단순히 일기보다도 뭔가를 다양한 방식으로 기록하는 것을 많이 해보려고 하고 있다.&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;팀이동&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;조직개편 될때마다 감정소모가 엄청 크다. 예전 팀이 너무 좋았으면 예전 팀사람들과의 관계를 끊어내지 못해서 지금 팀에 적응을 못하기도 하고, 다른 팀 사람들이 사이가 너무 좋아보이거나 문화가 좋아보이면 부러워서 자꾸 그사람들을 신경쓰느라 힘들어진다. &lt;br&gt;퇴사하는 사람들도 많고 2년동안 거의 4번넘은 조직개편을 겪다보니 이런 감정소모가 나를 너무 힘들게해서 이제는 조금 무던하게 반응해보려고 하고있다. &lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;저지른 행동들&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 뭔가 내가 의견을 내서 되는게 없는 것 같고, 어짜피 주어질테니 그냥 받아들여야겠다. 싶은 때가 있었는데 몇번 눈딱감고 저지른 행동들이 꽤 많은 결과를 내기도 했다.&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;저 xx팀 가기 싫어요&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;처음으로는 첫번째 조직개편때 딱 한 팀 가고싶지 않은 곳이 있었는데, 면담때 거기는 안갔으면 좋겠다고 일단 말해봤더니 다른 팀으로 갔다. 나중에 들어보니 내가 내정자였는데 내가 안와서 놀랐다는 ㄴ말도 들었다. 정말 다행이었다.&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;일정미뤄주세요 &lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;항상 일정이 픽스되고 거기서 공수산정(?은 그래서 왜하는지 모르게씅ㅁ)을 하고 개발을 시작했는데, 이미 픽스된 일정에서 아무리 생각해도 내 수준에 안되는 것 같아서 이거 일정 미뤄야된다. 하고 눈딱감고 질렀는데 미뤄졌다. 그 다음에도 이거 안된다. 하고 한번 더 미뤘는데 미뤄졌다.&lt;br&gt;일 하기 싫으면 미루겠다고 하고 일 미뤄라.가 아니고 팀장님들도 팀원들에게 정말 의견을 반영해주려고 묻는거니깐 내가 필요하면 목소리를 내야겠다고 느꼈다. 어짜피 못해줄거면 안들어준다. 내가 말 해도 안들어줄건 안들어줄테니 말안하면 후회할 것들은 말해보는게 좋은 것 같다.&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;발ㄹ표하기&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;뭔가 회사에서 작년에 일만하느라 기술적인 성장을 한게 별로 없는 것 같아서 사내 서버 개발자들 모임에서 spring modulith 발표를 했다. 이것도 원래 다른 선배개발자분이 도입하자고도 하고 발표하겠다고도 하던거 나한테 할래?물어봐서 할게요. 한거였는데 발표 자신도 없고 내용도 퀄리티도 별로 안좋을거같고 걱정이 많이 됐지만 지금 안하면 나중에 더 높은 연차돼서 부끄러울거 생각하니깐 해야겠다 싶어서 발표를 했다. &lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;대본이 있어도 국어책 읽듯이 읽지 말고 자연스럽게 말하기 라는 부끄러운 교훈을 얻었지만, 발표하는 경험도 좋았고 하면서 내가 정리하느라고 남은 자료도 나에게 있어서 재산이 될테니 좋은 것 같았다. 그리고 항상 완벽하지 않아도 된다는 생각으로 뭐든 해보고 좀 부끄러워보는것도 힘들지만 좋은 것 같다.&lt;/li&gt;&lt;/ul&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;심리상담받기&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 지원해주는 심리상담 프로그램이 있어서, 작년에 4회, 올해 8회차를 받았다. 올해는 지원이 끝나서 더이상 못받거나 사비로 받아야한다.ㅠ &lt;br&gt;'나는 왜 사랑할수록 불안해질까' 이 책을 읽다가 눈물이 나고 숨이 턱막혀서 그때부터 내가 어떤 사람이고 뭐가 날 힘들게하는지를 알려고 많이 노력했다. 인간관계나 가족관계, 일하는 방식, 나를 대하는 방식 모든것이 연결되어있었다. 상담을 받으면서 조금씩 나를 이해하고 있고 그것들을 회사에서 살아가는 경험에도 계속 녹여내려고 하고있다. 상담을 받았던 내용중에, 스크럼때 팀원분이 가볍게 던진 말이 나를 공격하는 말 같아서 너무 상처가되었다. 라고 말하니 그 이후에 내가 어떤 행동을했냐고 물어보셨다. 그냥 아무것도 안했다고 하니 그분은 내가 상처를 받았다는 사실조차 모를거라고 했다.&lt;br&gt;다음에는 '혹시 저 상처주려고 하신말인가요?'라고 물어보던가, '그런말 하면 상처예요.'라고 말하면 그분도 깨닫고 안하던가, 실수였다면 사과를 할거라고했다. 한번도 이런 방식으로 표현하고 해결해본적이 없었는데, 그 이후로 조금씩 내 마음을 가볍게 드러내는 법을 연습하고 있다. 회사에서는 감정적으로 실수를 하는게 좋지는 않지만, 정말 다양한 성격의 사람들이 몰려있기 때문에 내가 당연히 관계를 이어나가기 어려울 수밖에 없다고 생각한다. 그래서 조금씩 경험해보면서 여러 사람들을 건강하게 대하면서 나를 지켜가는 방법을 만들어나면 되는 것 같다. &lt;/p&gt;&lt;h1 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;span&gt;기억하고 싶은 소재&lt;/span&gt;&lt;/h1&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;나의 업무 방식 - 문서화&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;나는 문서화를 되게 중요하게 생각한다. 이 이유는, 첫 회사에서 공유의 문화가 너무 적었고 신입으로 입사했던 나에게 너무 가혹한 환경이었기 때문이다.&lt;br&gt;배정된 사수님은 모든 문서를 비공개로 저장해두었다. 아무도 알려주지 않았던 업무를 맡고 혼자 끙끙 앓다가 사수님께 질문했더니 사수님의 개인 문서를 보여주며 나한테 설명해준 적이 있었는데, 그때부터 나는 그냥 다 열어놓는 사람이 되어야겠다고 생각했었다.&lt;br&gt;물론 지금은 그렇게 비공개 문서를 작성한 것 자체에 불만이 있지는 않다. 다 개인만의 스타일이 있는거고 그 자체를 공유하지 않는것이 잘못은 아니니깐&lt;br&gt;여튼, 너무 주어진 자원이 없는 곳에서 살아남다 보니 내 후배가 들어왔을 때는 최소한 나보다는 좋은 환경에서 온보딩이 되길.하는 바람으로 시작했던 문서화가 이제는 나의 습관, 심할 땐 강박이 되었고 어쨌든 업무에서 나의 장점이 되었다.&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;나의 업무 방식 - 노트&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;나는 아직도 일할 때 작은 노트와 펜을 들고 다닌다. 이것도 첫 회사에서 자리에 있는 데스크탑밖에 없었어서 회의들어갈 때 노트를 들고다니던 습관이 시작된거지만, 대학시절 공부할때도 노트에 내 글씨로 정리하는걸 좋아했고, 내 손으로 적어야 머리에 들어오는 느낌이 있었다. &lt;br&gt;지금 회사에 와서도 1~2년차때는 특히 일을 할때 모든 것들을 노트에 적었다. 주로 내가 프로젝트를 리딩하는 역할이 아니었기 때문에 앞에서 말해주는 분 뒤에 숨어서 그들이 하는 말들을 다 적고 자리에 돌아와서 이해하고, 위키에 정리하면서 업무를 익힌 것 같다. 그 덕분에 내 노트나 내가 남겨둔 회의록에서 히스토리를 찾아내는 경우가 많았어서 되게 도움이 되었던 것 같다. &lt;br&gt;가끔은 그냥 노트북에 적으면 되는걸 노트-&amp;gt;노트북으로 다시 옮기는 수고로움을 왜 하느냐고 하는 사람들도 있었는데, 나도 아는데 잘 안고쳐진다. 그리고 어디가서 노트에 적으면 의도했뜬 안했든 열심히하는것처럼 보니깐 이미지메이킹에도 도움이 된다.(ㅋㅋ) &lt;br&gt;요즘엔 연차가 좀 찼다고 뒤에서 적기보단 말할일이 더 많아져서 노트쓰는게 점점 줄어들기도 하고 놓치는게 많아지는 것 같아서 슬프지만, 그만큼 노트에 적지 않아도 이해가 바로 될만큼 업무에 적응이 되었다는 것 같기도해서,, 좋은 습관이니 지우지는 않도록 의식해서 노트를 놓치않으려고 하고있다.&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;나의 업무 방식 - 업무 일지&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;업무일지를 처음 쓰기 시작한 것은 입사한 해 12월이었다. 이 회사에 적응을 할지, 다시 공부를 해서 이직을 해야할지 갈피를 못잡던 시절에 힘듦이 찾아왔고 아 이 회사를 떠나야겠다. 라고 깨달아서 지난 10개월을 돌아보니 뭘 했는지 남은게 없고, 기억도 안나고, 무서웠다. 그래서 내가 오늘하루 무엇을 해냈는지를 적으면서 그걸로 이력서를 마련해야겠다는 생각으로 쓰기 시작하던게 이제는 그저 습관이 되었다. 업무일지를 쓰고 나니 지금 회사를 와서도 매년 평가시즌에 내가 무엇을 했었는지 좀 더 상세히 기억해낼 수 있어서 좋은 것 같다.&lt;br&gt;요즘에는 업무일지를 쓰는 방식도 다른 사람들의 방식을 이것저것 주워다가 다양화 시켜보고 있다.&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;나의 업무 방식 - 반복 속에서 발전하기&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;첫 회사에서 이직을 할 때 가장 도움이 되었던 프로젝트이자, 나에게 경험을 안겨줬던 부분이 매일 반복되고 의미없었던 업무속에서 내가 할 일을 찾아내는 것이었다. 매일 타 팀에서 업무 요청이 들어오면 매뉴얼을 보며 똑같은 VM서버를 생성하는 일을 했었는데, 딸깍딸깍 복붙의 반복이었다. 항상 똑같은 일을 하는데도 매뉴얼이 없으면 일을 하지 못했다. 이게 맞나?하고 현타가 와서, 안보고도 할 수 있으려면 이걸 내가 이해하고 있어야겠다. 라는 생각이 들어 업무가 들어올 때 마다 5%씩이라도 이해해보려고 노력했고 20번이 채워지면 100%가 된다는 생각으로 반복속에서 수련을 했다. (지금 보니 함께자라기 책에 나오는 의도적 수련의 방법을 알게모르게 하고있었던 것 같다.) &lt;br&gt;이 경험을 하면서 결국 VM -&amp;gt; AWS EKS로 옮겨가는 과정에서 cloudformation 을 사용한 자동화 개선을 해냈고, 그걸로 뭔가 나 스스로 뿌듯한 결과를 (그리고 이직할 때 써먹을만한 키워드를 만들어냈다)는게 나름 좋았다.&lt;br&gt;지금 회사에서도 가끔은 json 상하차라고 우리끼리 우스갯소리로 하듯이 생각보다 단순한 업무의 반복이 될때가 많다. '반복'을 그저 '반복'만 하다보면 정말 무료해지고 나의 존재가치가 없어진다. 그래서 내가 지금 반복되는 업무속에서 개선할 수 있는게 무엇일까를 항상 찾으려고 노력한다.&lt;br&gt;새로 생성한 MSA 서버에서 최대한 컨벤션을 맞춰가며 개발하기, 테스트코드 꼭 작성하기, 이슈 업무 일지 템플릿 계속 개선하기, 들어온 일과 관련된 도메인이나 키워드 공부해서 위키에 정리하기. 이런식으로 조금씩 수련하는 습관을 들이고있어서, 그나마 나만의 재미를 찾아가는 것 같다.&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;기록을 중요하게 여기게된 이유, 습관&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;위에도 적었지만, 나는 문서화를 되게 중요하게 생각한다. 첫번째로는 그때 고통받았던 나 같은 사람을 위해서고, 그 다음엔 나도 내가 했던 일을 까먹기때문에 미래의 나를 위해 내가 남기는 선물이라고 생각한다. &lt;br&gt;그리고 모든것을 기억할 순 없지만 기록해두면 그것들은 언제라도 꺼내서 기억할 수 있기때문에 기록이 정말 중요하다고 생각한다.&lt;br&gt;초등학교때부터 메이플 블로그로 게임 일상을 기록하며 이웃들과 커뮤니티를 만들어 놀았었는데, 어느날 메이플을 접는다고 블로그 초기화를 했다가 두고두고 후회한적이 있다. 그래서 그때부터 컴퓨터에 기록해둔 것들을 비공개는 해도 삭제는 잘 하지 않는다. &lt;br&gt;핸드폰이나 디카를 다루기 시작한 이후 2012년쯤부터의 사진들은 다 백업을 해둬서 가지고 있다. &lt;br&gt;고등학교시절에 음식 후기 블로그를 1년정도 한적이 있는데, 가끔 보면 너무 추억이 돼서 재밌었다. 그래서 대학시절 일상을 개인 블로그에 기록해뒀는데, 시간이 지나서 내가 보면 너무 재밌고 귀엽고 추억이 됐다. &lt;br&gt;내 기억속에 남은 모든 스냅샷들은 프로필사진을 했든, 페이스북에 올렸든, 인스타에 올렸든, 블로그에 올렸든 어딘가에 기록해둔 것들만 결국 기억이 난다. 그리고 그 기억을 믿고 머리를 좀 비워내니 새로운 추억들을 기억하기에도 수월해진다. &lt;br&gt;그래서 꼭 공부, 개발이 아니더라도 웬만하면 기록을 많이 해두려고 하는 것 같다. &lt;br&gt;기술블로그, 인스타, 개인블로그, 개인 아이패드 등등 이곳저곳에 나의 기록하는 공간들을 많이 만들어뒀고 앞으로도 그곳들에 계속 기록해두면서 내가 어떻게 1년 1년을 보내왔는지 적어둘예정이다.&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;번아웃&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;요즘의 나를 가장 크게 달라지게 만든것이, 번아웃의 경험인 것 같다.&lt;br&gt;24년 8월쯤 번아웃이 정말 쎄게 왔다. &lt;br&gt;나는 대학교, 첫번째 취업, 이직 그리고 작년까지도 그냥 항상 열정적이고 바쁘고 열심히 살아왔다. 그렇게 사는 법 밖에 몰랐고 항상 뒤쳐질까 불안했고 뭐든 하고싶은게 참 많았다. &lt;br&gt;23년도쯤에 친해진 동료들이 '00는 쉬는법을 모르는 것 같아' '개인시간을 좀 가져' '하루종일 회사에서 일하다 운동하고 집가선 잠만자네' 이런말을 했다. 나는 그게 무슨 문제인지도 모를만큼 그냥 그게 당연한듯이 살았다.&lt;br&gt;회사에서 일을 열심히하고, 성과를 내면 그게 내 성공 지표가 되는 느낌이었고, 23년도에 빠진 운동이 내 취미가되어서 뭔가 해낸다는 뿌듯함을 운동으로 얻어내며 오늘 하루도 불태웠다..하고 집에서는 진짜 잠만잤다. 혼자 노는 법도 몰랐고, 혼자 지내는 시간의 필요성도 몰랐다. 그러다 가끔씩 친구들 만나서 술마시고 정말 피곤하면 1~2달에 한번 하루종일 잠만자면 충전이 됐었다.&lt;br&gt;그런데 몇번의 변화가 찾아왔다.&lt;/p&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;혼자 있을 때 너무 외로워한다는 것을 깨달았다.&lt;/li&gt;&lt;/ol&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;내가 개인시간의 필요성을 못느낀것은, 혼자 있는 개인시간이 너무 무섭고 재미없고 어떻게 보내는지도 몰랐던 것 같다. 뭔가 집에 가만히 누워만 있으면 불안해서 계속 밖으로 나갔고 밖에 나가면 할줄아는게 없어서 친구를 만났고 친구들이 바쁘니 일을 하거나 운동을 했다. &lt;br&gt;23년도 초에는 외로움이 너무 극에 달해서 힘들었던 시절도 겪고 이별도 하고 그러다보니 뭔가 내가 정말 혼자 못보내고있었구나를 깨달았다. 그래서 닌텐도를 충동구매해서 동물의 숲도 해보는데 그것도 별로 재미있지 않아서 금방 접었고, 일단 맨날 하던 것 처럼 일을 열심히 했다.&lt;/p&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;일=나 처럼 생각했다.&lt;/li&gt;&lt;/ol&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;지금 회사에서 처음 제대로 맡는 프로젝트가 나한테 너무 잘맞았다. 너무 꼼꼼하고 실력좋은 기획자분과, 정말 잘하는 동료들과 함께여서 배울점밖에 없었고 마침 프로젝트도 너무 나랑 잘맞는 프로젝트여서 하는 내내 행복했고 애정을 가득 담았다. 프로젝트 개발, 후속 기능 개선, 추가작업 등등 2년정도 하면서 이 프로젝트와 관련된 일에는 항상 발벗고 나섰다.&lt;br&gt;또 다른 프로젝트들이 들어왔을 때, 그냥 정말 열심히 했다. 그런데 열심히 하던 와중에 내 생각보다 잘 안풀리는 프로젝트들도 많았다.&lt;br&gt;여틍ㄴ!! &lt;br&gt;샤워하면서도 회사생각, 일어나서도 회사생각, 꿈꾸면서도 회사생각을 하다보니 본가에있는 엄마한테 '잘 지내니?'연락이 와도 화가나서 싸우기도했다. 이러면 안될 것 같아서 극복이 필요하다고 느꼈다. 포기선언을 했다.&lt;br&gt;이제는 번아웃이 오지 않기 위해 밸런스를 좀 맞추려고 하고 있다. &lt;br&gt;'내가 없이도 회사는 잘 돌아간다' '하고싶고, 할 수 있다고 다 할필요는 없다.' &quot;근무시간 내에 해내는 것이 가장 좋다&quot;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로젝트 리딩 첫 경험&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;개발 하던 중에 일정이 미뤄지기도 했고, 조직개편이 되어서 애정을 담던 프로젝트와 서비스를 다른 팀에 보내줘야하기도 했다.&lt;br&gt;그리고 내가 프로젝트의 '부'가 아닌 ''정'으로 일하게 된 24년도부터는 점점 더 힘들어졌다. '나' 그 자체였던 '일'이 맘에 들지 않으니, 내 자존감도 떨어지고 하루종일, 한달 내내 기분이 나빴다. 프로젝트를 편식하는 경향이 있었던 것 같다. 그리고 내 앞에서 안되는 일을 쳐내주고 일정을 파악해서 커뮤니케이션을 해주던 시니어분들이 없이 내가 프로젝트를 맡다보니, 내가 해야하는 정도를 정하지 못해서 헤맸던 것 같다.&lt;br&gt;프로젝트를 하다보면 각자 자신의 역할이 있고, 해야할 범위가 있는데, 첫번째 프로젝트에서는 모두가 자신의 역할을 넘어서 했지만 모두가 그랬기때문에 그저 발전하기만 한 프로젝트였다. A 프로젝트를 할땐 원래 내가 하듯 내가 하던 역할이상을 해냈더니, 사실 그 일은 내 일이 아닌데 다 나한테 왔다. 이게 누구의 역할인지를 모르는 주니어들끼리의 프로젝트였기때문에 누가 잘못이라고 할 수는 없겠지만, 나는 힘들었다. 그런데 그냥 해냈지만 그때 너무 벅차서 계속 화가 나있었다. &lt;br&gt;B 프로젝트를 할땐 나는 이제 프로젝트에 애정을 담지 안기로했어. 집에 가서도 생각하고 그러니깐 너무 지쳐. 하면서 딱 1인분만 하려고 했다. 그런데 내가 '부'가아닌 '정'이었기 때문에 내가 해야할 역할이 사실 좀 더 많았는데도, 내가 내 역할을 다하지 않아 뒤에 급하게 처리해야될 것들이 많아졌다. 기획서를 꼼꼼히 보지 않아서 파악이 되지 않았떤 것도 있었고, 실질적인 프로젝트 개발 리더가 나였음에도 내가 그 역항르 해내지않아서 결정을 하지 못하는 경우도 잉섰다 ㅇ&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;완벽과 최선의 딜레마, 고민의 연속, 걱정&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;일하다보면 '클린코드' 'DDD' '완벽한 컨벤션' '최선의 선택'&lt;br&gt;A안과 B안이 있는데 하나는 서버에서 불편하고 하나는 클라에서 불편하다, 사실 두개중에 뭘 해도 괜찮고 우리끼리의 선택이다. &lt;br&gt;아니면 뭔가 컨벤션이 있는데 그걸 깨트리는 방안을 선택해야한다. 이럴때 항상 최선의 선택이 뭘까를 정말정말 고민했다.&lt;br&gt;아직도 그게 나쁘다고 생각하진 않고, 언제든 가장 좋은 방법을 택하긴 할거다.&lt;br&gt;그런데 그땐 맞았지만 지금은 틀린게 있을것이고, 항상 완벽한 선택을 하려다보면 너무 피곤해진다.&lt;br&gt;그리고 내가 '리딩'을 하는 입장이라면, 누군가에게 선택지를 주는 것 보다 그냥 '결정'해주고 그 '결정'에 책임을 지는게 더 나을때도 있는 것 같았다. &lt;br&gt;그래서 일 할때 너무 고민하지 않기로 했다. 적당한 고민은 중요하지만, 일이 늦춰질만큼 고민하면 신중한 사람이 아니라 그냥 일 느리게하는 사람이 된다. &lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;포기해도 괜찮다, 실패해도 괜찮다는것을 처음 느끼게됨&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 리딩하면서, 일정을 2번을 미뤘다.&lt;br&gt;항상 모두가 빡빡한 일정도 다 해내는 분들이었어서, 나는 그게 당연하다고만 느껴졌고 정해진 일정과 지금 할 수 없는 리소스에서 내가 할 수 있는건 '야근'이라는 선택지밖에 없다고 생각했다.ㅏ 그러다가 번아웃이 왔다.&lt;br&gt;'내 주제'를 알고 , 내가 할 수 있는 최선을 다하겠지만 (번아웃이 오지 않을 정도로) 내가 할 수 있는 정도가 넘어간다면 좀 포기해도 괜찮은 것 같다.&lt;br&gt;먹고 살려고 하는 일인데, 일하면서 스트레스 받고 잠도 잘 못자고 서로 감정 상하면서 힘들 필요가 뭐가 있나 싶다. &lt;br&gt;이거 저는 일정내에 못해요. 일정 미뤄주세요 라는 말이 저는 형편없는 개발자예요. 남들은 다 하지만 저는 이 기간내에 코드를 짤 수 없어요. 라고 말하는 것 같아서 무서웠는데, 그냥 용기내서 패배선언을 했다. 정말 슬펐다.&lt;br&gt;그런데 패배선언 덕분에 많은것을 얻었다. 같이 일하는 분들이 질문하러 오거나, 뭔가 추가 스펙을 들고오면 그냥 미워서 다 싫었는데 그런 마음도 사라졌다. 그리고 그래도 괜찮구나. 하는 그 마음을 처음 얻었다. &lt;br&gt;매일 미루고, 대충하면 안되지만 열심히 해왔기 때문에 이번에는 다들 이해해준거라고도 생각한다..&lt;br&gt;## &lt;br&gt;뭔가 완벽하려고 하지말고 코드리뷰도 그때그때 잠깐 해보고, MR도 그냥 올ㄹ려보고, 이런걸 좀 해야할거같다 모든걸 다 파악하려고 하는것도 내가 능력이 되면 괜찮지만 능력이 안될때 그걸 하려고하면 실패했다는 마음에 오히려 날 더 힘들게 할것이다. &lt;br&gt;그리고 이때 프로젝트를 하면서 너무 미워지는 사람이 있었다. 사실 그사람이 잘못한게 아닌데도, 내가 너무 힘든데 나를 힘들게 하는사람이라는 이유만으로 그 사람을 대하기가 힘들고 뭐만하면 화가났다. 인간적인 문제가 아니고 프로젝트를 하다가 생긴 이 화를 그 사람한테 내는게 맞는가 하는 생각이 들어서, 그냥 못하겠다고 던졌고 팀장님들 선에서 다시 논의가 되어서 프로젝트 일정을 정리하고, 다시 바로잡았다. 그러고 나니 불편한 상황이 해소되어서 그분에게 악감정이 사라졌다. &lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;1년동안 한게 없었다고 불안해했지만 꽤 많은걸 해왔다는것들로 나를 다독이기&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;이 회사를 와서, 생각해보면 정말 많은 것들을 배우고 많은 경험을 했는데 1년을 돌아보면 괜히 너무 한게 없는 것 같아 무섭다.&lt;br&gt;그럴때마다 업무 일지 한번 읽어보거나, 그동안 스터디했던 기록들을 살펴봣는데&lt;br&gt;정말 한게 없다고 느낀 23년에는 알고보니 코틀린이랑 헥사고날 아키텍처를 처음 배우고 새로운 프로젝트에 도입해봤다. 스터디하는 초반에는 정말 무슨말인지 이해도 못하겠고 새로운 언어에 부담이 컸는데 이제는 너무 익숙해서 그게 내가 '한거'라고 느껴지지 않는 거였다.&lt;br&gt;그래서 기록이 더 중요한 것 같았고, 내가 뭔가 못했다고 생각하면서 너무 슬퍼하지 않아야겠다고 생각했다.&lt;br&gt;그리고 혹시나 너무 발전한게 없더라도 괜찮다고 생각해야겠다. 유지하는 것도 에너지가 든다. 그리고 개발자로서 엄청나게 기술적인 성장을 하는것도 항상 중요한 부분인 것 같지만, 나는 일개 회사원이고 이제야 사회생활을 막 적응해가는 사람이니깐 회사 일 외에도 나를 알아가고 내 주변 사람들과 관계를 지켜가고 건강을 챙기는 것도 중요한 것 같다. &lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;회사에서 조직은 영원하지 않다&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;같이 정말 열심히 프로젝트를 했던 기획자분이 돌연 퇴사소식을 밝히시고 2주 뒤에 떠나셨다. 그 소식을 작년 말에 듣고 진짜 그 자리에서 엉엉 울었다. &lt;br&gt;그 분과는 한번도 개인적으로 커피를 마셔보지도, 밥을 먹어보지도 않았다. 그냥 2년 내내 일만 열심히했고 항상 서로 뭐라도 하려고 노력하는 사이였다. 그래서 그냥 내적인 친밀감만 가득했던 것 같다. &lt;br&gt;그분이랑 내년에는 이 프로젝트에 이거 개선해야지, 다음에는 이거해야지, 이런 마음속 혼자만의 계획이있었고, 언젠가는 같이 밥먹으면서 수다도 떨어봐야겠다. 라고 생각했는데 그 '언젠가'와 '다음'이 사라진다는게 너무 슬펐다.&lt;br&gt;그 서비스를 개선할 사람이 이제 사라지겠구나. 내 아이는 이제 버려지겠구나, 하는 스픔도 있었고&lt;br&gt;항상 같은 조직, 같은 회사에 같이 있을거라고 생각하고 미뤄왔던 관계들이 한순간에 다음은 없어질수도 있겠구나.하고 큰 깨달음을 얻은 것 같다.&lt;br&gt;원래 점심시간에 운동이나 취미생활, 아니면 혼자 밥먹고 산책하는 시간을 즐기던 편이었는데 그 퇴사 이후로는 친해지고 싶었떤 분들이랑 밥도 좀 먹으려고 노력하고 개인적으로 알아가고 싶은 분들이랑은 뭔가 더 시간을 내서 관계를 쌓아가려고 노력하고 있다. &lt;br&gt;퇴사하신 분이랑도 따로 연락해서 밖에서 밥도 먹었다! 항상 영원한건 없지만 내가 노력으로 유지할 수는 있는 것 같다. &lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;가스라이팅 당하지 말자&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;전 회사에서 입사하고 1년동안 정말 수많은 힘듦을 겪었다. 뭔가 회사 분위기도 너무 힘들고, 적응도 안되고 일도 안맞고 너무 이상한데 나빼고 아무도 이상함을 느끼지 못했다. 그 좋은 회사 들어가서 왜 나오려고하냐, 너보다 힘든사람 더 많다, 너가 좀 더 적응해봐라, 다른 회사 가기 어렵다, 기타 등등 주변에서도 그런 얘기들을 아무렇지 않게 하고 주변 친구들을 보면 회사에서 나처럼 적응을 못하고 힘들어하는 사람은 없어보여서 말이 안통했다. &lt;br&gt;그래서 내가 잘못인가?하는 생각에 매일매일을 자존감도 깎이고 힘들었다. 퇴근하면서 매일 엄마한테 전화해서 운거같다. &lt;br&gt;이정도까지 나를 가스라이팅 하는 상황에 주어지면 아무리 내가 빌런이거나 잘못을 했을수도 있더라도 그 환경이 문제라고 생각한다. 그 회사에서 말고 다른 친구들과는 그때도 지금도 항상 잘지내고, 이직을 해보니 그 시기에 나를 필요로하는 회사는 다른데도 많았고, 지금 회사 사람들은 나를 정말 좋아해준다. &lt;br&gt;내가 잘못이 아니었다. 날 힘들게 하는 환경에 가스라이팅 당할 필요는 없다.&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;기분이 태도가 되지 말자&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;작년에 가장 나에게 문제가 되었던 것은 나의 태도였다고 생각한다.&lt;br&gt;남들이 따로 말하진 않았지만,, 나 스스로 엄청 반성하는 부분이다.&lt;br&gt;번아웃이 오고나서 친절가면을 벗었다. 예스맨으로 살지 않기로 하고 그냥 무슨 질문이 들어와도 퉁명스럽게 대하면서 안된다고 하던 시절이있었다.&lt;br&gt;그리고 나는 열심히 하는데 말도안되는 얘기를 하는 사람들을 보면 화가나서 자꾸 나쁜 태도가 나왔다. 감정조절을 너무 못했다.&lt;br&gt;그러고 나서 바로 자책하고 후회햇다. 그리고 어떤분께서는 직접적으로 아까 그렇게 말하신거 기분 나빴다고도 말해주셨었다. &lt;br&gt;아직도 감정조절은 힘들지만 기분이 태도가 되지 않는게 정말 중요하다고 느꼈다. 서로 그냥 잘 하자고 하는 일인데 실수든, 의도든, 몰라서든, 알아도든 내가 그렇게까지 감정적으로 받아들일 이유는 없는 것 같다. 사람들이 날 찾으러 왔을 때 불편하게 대하면 1년만 지나면 아무도 날 찾으러 오거나 나한테 도움을 요청하지 않는 외로운 사람이 될 것 같아서 이제는 반성하고 다시 친절해지려고 한다..&lt;br&gt;나는 항상 다른사람들 자리에 갑자기 가서 물어보기도 하고, 놀러도 가고 하는 편이었는데 막상 내가 바빠지니 다른사람들이 내 자리에 와서 뭘 질문할때 ㅓㅇㅁ청 예민하게 대하고있었다. 내가 해놓고 그런 내 모습을 보니 너무 미웠다. 그래서 항상 친절하게 받아주는 팀원들에게 감사하면서 나도 그래야겠다고 생각했다. 정 친절할 에너지가 없으면 그냥 휴가를 쓰고 퇴근을 해라. &lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;이직, 이별&lt;/h2&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;우선순위 관리&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;일할 때 우선순위 관리를 잘 못한다. 그전까지는 내가 하는 일이 그렇게 중요하거나 크지 않았고 일정도 여유로워서 내 주특기인 쓸데없는거에 파고들기를 해도 시간이 괜찮았는데, 점점 내가 해야할일이 많아지니 파고들기를 우선순위를 높게 잡고 일하니 일저에 차질이 ㅅ애겼다.&lt;br&gt;프로젝트에 APi이관과 기능개선, 추가작업, 기존 스펙 분석들이 있었는데 기획서가 아직 픽스되지 않았다는 이유로 그 시간에 이후에 해야할 프로젝트 개선이 아니라 내가 하고싶은 API 이관쪾에 시간을 잡았다. 그러고 한달이 흘러서 보니 물론 나한테도 ㅎ회사에도 그걸 조사했다는게 도움은 됐겠지만 조금 나중에 했어도 좋았을 걸 하는 새각을 했다.&lt;br&gt;중요한건, 중요하지않지만 급한것, 중요하고 급한것, 중요하지만 급하지않은것 들을 잘 관리하는 살마이 되어야겠다. &lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;완벽주의&lt;/h2&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;그땐 맞았지만 지금은 틀린것, 그땐 틀렸지만 지금은 맞은것&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;항상 존재한다. &lt;br&gt;클린코드, 확장성, 뭔가 그 시절에 최선의 선택으로 만들었지만 지금은 그대로 할 수 없는것들에 나는 너무 힘들어했다.&lt;br&gt;그땐 저랬는데 왜 지금은 안돼?! 전에 이랬으니 지금도 이래야해!!&lt;br&gt;내 스스로으 ㅣ주관이 없으니 항상 과거의 기록에 의지해서 해서 고집을 꺽기가 힘들었다.&lt;br&gt;그리고 항상 확장성을 고려해서 개발했지만 나중에 추가스펙이 들어와보면 그 확장성을 이용한 확장이 불가능한 경우도 많았다. &lt;br&gt;그래서 좋은 코드 작성도 중요하지만 처음이나 두번째까지는 너무 코드퀄리티에 집중하지 않는것도 방법인 것 같다. &lt;br&gt;지도선배님이 너무 나한테 안알려준다 싶었는데, 그냥 내가 그 직무에 마음이 안들어서 좀 더 안좋았던것도 있었을 것 같다. &lt;br&gt;팀이 바뀌었더니 너무 팀에 관심없는 사람들이 있었는데 지금 보니 왜 그런지 알 것 같다.&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;읽은 책&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;개발 도서 &lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;회사에서 스터디를 하면서&lt;/li&gt;&lt;/ul&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;회사에서 직접 배운 경험들&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;모닝터링&lt;br&gt;프로젝트 진행 방식, &lt;br&gt;배포 방식&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;조급함&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;팀에 새로운 사람들이 들어오면 정말 대단한 사람들이 많다는게 느껴진다. 그리고 기술적인게 아니더라도 정말 배우고싶은 점이 많은 사람들이 많다. 나는 왜 그렇지 않을가?하는 불안함과 조급함이 나를 힘들게 하기도 한다. 항상 그런다. 그래도 그럴때마다 그사람이 그걸 지금 멋지게 하는 이유는 정말 오랜 시간을 들여서 자기거로 만들었기 때문일거야. 나는 그 분야에 그만큼 시간을 들이지 않았으니 비교되는게 당연해. 그런데 나도 내가 그만큼 시간을 들인 분야에 대해서는 뛰어날수있을거야. 하고 좀 다르게 생각하려고 노력하고있다.&lt;br&gt;예를들면 나와 성향이 비슷해서 기록을 꼼꼼히 잘하시는 분이 있는데, 나보다 퀄리티가 훨씬 좋으셨고 그분은 책도 많이읽으셔서 책읽은거에 대해서도 본인만의 스타일로 엄청 기록을 잘하시는 분이 있었다. 너무너무부럽고 따라하고싶어쓴데 따라할수가 없었다. 그래서 그분을 보고 그냥 서로의 다름을 인정하기로 했다. &lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;다양한 사람들&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 어쨌든 사람들에게 친절한건 항상 좋다. 다정함은 체력에서 나온다. 친절은 정말 에너지르 ㄹ써야하므로 나에게 친절한 살마들에게 항상 감사해야한다.&lt;br&gt;그리고 사람들간의 성향이 개발자들끼리도 정말 다르다. 개발을 정말 좋아하는 사람들, 개발을 잘하지만 퇴근하면 그냥 쉬는 사람들, 발전하는 사람들, 노는걸 좋아하는 사람들, 뭐..등등 &lt;br&gt;팀원들의 성향을 파악하ㅗ 적응하는동안 우여곡절이 있어쓴데 결국 나도 좀 어른이 되고 그들과도 오랜 시간이 지나고나니 그냥 모두 다 틀린것도 없고 다양한 사람들이고 모두의 방식이 맞다. &lt;br&gt;엄청 진보적인 분이 하는 모든게 너무 힘들어서 솔직히 짜증도 났었는데, 나중에 보면 그분 덕분에 엄청 빠르게 발전했고 좋은 기술들이 많았다. 내가 조금 더 그때 그분의 마음을 알았떠라면 같이 으쌰으쌰했을텐데 한창 열정적일 때 팔로워가 없어서 기죽었을까봐 이제와서 좀 죄송하고 그래도 항상 열심히해준것에 고마웠다.&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;정보의 과잉&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;가끔은 나의 불안함이 정보의 과잉이라는 생각이 들기도 했다. &lt;br&gt;내 좌우명이 우물안 개구리가 되지 말자였는데, 이젠 자꾸 우물 밖의 세상을 궁금해하는것도 그렇게 좋지 않다는 생각도 든다.&lt;br&gt;수많은 정보들속에 내가 정말 지금 필요로하고 나를 발전시킬 수 있는건 한정될텐데.&lt;br&gt;옛날에는 내가 모르는게 없었으면 좋겠다고 생각했는데, 이젠 적당히 알고싶고 차라리 모르고 싶은 정보도 많다. 때가 되면 알게 될텐데 너무 많은 정보를 한번에 소비하려고 하니 소화시키지 못하고 체하는 것 같다. &lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;항상 내가 하고싶은 것만 할 순 없다.&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 엔지니어 가이드북인가 함께자라기에 내용잉 ㅣ씀.&lt;br&gt;어쨌뜬 나는 월급을 받고 일하는 입장이기때문에 팀장님이 내 의견을 들어주시기도 하지만 항상 하고싶은것만 할 수 없다는 걸 생각해야한다. 그래서 첫번째 직장에서 내가 할수있는 일을 하지 못하는게 너무 슬퍼서 결국 뛰쳐나왔지만, 만약 내가 이직을 하지 않았더라면 어땠을까를 생각해 봤을때 그래도 그 자리에서 꾸준히 뭔가 열심히 했다면 내가 하고싶은걸 시켜주는 환경이 나왔을거라고 생각한다.&lt;br&gt;지금도 작년에 내가 목소리를 낸걸 팀장님들이 들어준 이유는, 그냥 팀원이 해달라고 해서가 아니고 하고싶은것이 아니어도 꾸준히 성실하게 해왔기 때문에 그것들이 쌓여서 한번의 찬스를 쓴거라고 생각한다. &lt;br&gt;올해도 조직개편이 되어서 새로운 팀에서 새로운 일을 해야하는데, 뭔가 썩 하고싶은일이 있지 않았다. 덜 하기싫은일과 좀 더 하기싫은 일이 있다. (요즘 왜이렇게 일하기가 싫은지 모르겠다.) 그런데 그냥 덜 하기 싫은 일을 선택했는데, 들어주셨다. 혹시나 여기서 내가 원한것과 다른게 되었더라도 그때 너무 삐져서 일을 안하면 그 이후엔 내가 진짜 하고싶은 일을 할 기회가 주어지지 않을 것 같기도 했다. 그래서 앞으로도 항상 하고싶은것만 할 순 없다.라는 생각으로 너무 찡찡대지 않으려고 노력하려고 한다.&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;일단 하기 &lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;나는 걱정이나 생각이 너무 많다. 불안함이 너무 많다. 버크만 검사를 했는데 나의 성격이 이렇다고나왔다. 2025년의 나는 걱정과 불안을 좀 내려놓고 '일단하기'에 초점을 맞춰서 지내봐야할 것 같다. &lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;내려놓기&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 강박적응로 뭔가 팀에서하는 프로젝트를 다 알아야하고, 다른 팀이 하는것들도 다 알아야할것같고, 같은 프로젝트안에서 일어나는 일을 다 케어행햐라것같은 마음이 든다. 그래서 엄청바쁜데 그게 가능하다면 하면 되지만 내 지금 역량상 그게 불가능하다는걸 알았다;이럴땐 좀 불안하고 조급한마음이 들어도 그냥 내가 할 역할만 일ㄴ단 ㅁ너저 하고, 그 다음에 여유가 생기면 다른 곳을 챙기는 것을 좀 해야할 것 같다. 여기서 우선순위를 바꿔서 다른 것들을 먼저 하고 내가 해야할일을 뒤로 미루면 나는 일을 못하는 사람이 되는거니깐. &lt;br&gt;2021&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;삼성 첫 입사. cloud 맛보기. 슈퍼루키. 롤 골드찍기. 마요롤 개발하기. 뭔가 우울한 1년이었음&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;2022&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;개발공부 정말 열심히하기 이직하기 진짜 신났다. 새로운 개발 경험들 &lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;2023&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;최애적금. PT 열심히 한것. 이별과 만남. 조직개편. 질투. 코틀린 헥사고날 익히기&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;2024&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;번아웃. 드럼. 호주. 책읽기시작. 나를 찾아보기. 프로젝트 리딩&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;2025&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;커리어 고민들. 이것저것 새로운 해보고싶은 것들 찾아보기. 불안함을 견디는 방법&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;₩&lt;/p&gt;&lt;h1 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;span&gt;부록&lt;/span&gt;&lt;/h1&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;좋은 개발자를 바라보는 다양한 시선(김다현 네이버 시니어 엔지니어)&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;팀원이 생각하는 '잘함'의 기준과 매니저가 생각하는 '잘함'의 기준은 사뭇 다르다. p516&lt;br&gt;매니저의 관점에서 좋은 개발자란 코드를 잘 작성하는 능력 이상의 자질을 갖춘 사람입니다. p516&lt;br&gt;매니저는 효과적인 의사소통 능력 또한 높이 평가합니다. 비기술 업무를 수행하는 동료에게 복잡한 기술적 내용을 이해할 수 있게 설명하고, 프로젝트의 진행 상황과 잠재적 리스크를 명확하게 전달하는 개발자가 좋습니다. p517&lt;br&gt;동료들의 시각에서 좋은 개발자는 기술적 능력과 더불어 뛰어난 대인 관계 기술을 갖춘 사람입니다. p517&lt;br&gt;지식과 경험을 공유하는 문화를 만드는 개발자 p518&lt;br&gt;주니어 개발자 단계에서는 기본기를 탄탄히 다지는 것이 중요 p518&lt;br&gt;코드 작성 능력 말고도 협업 기술과 커뮤니케이션 능력도 함께 발전시키는 편이 좋습니다.&lt;br&gt;시니어 개발자로 성장하면 기술적 전문성과 함께 비즈니스에 대한 이해도를 높이는 것이 중요합니다. p519&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;개발자의 역할 : 기술과 사람의 만남 (김현태 헤렌 CTO)&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;No Silver Bullet 소프트웨어 개발의 복잡성을 해결할 수 있는 '마법 같은' 방법은 없다고 주장 p521&lt;br&gt;언제나 통용 가능한 유일한 해결책이 존재하리라 기대하는 것은 현실적이지 않습니다. p521&lt;br&gt;콘웨이의 법칙 (시스템을 만드는 조직은 그 조직의 의사소통 구조를 따라가게 된다) ↔ 역콘웨이의 법칙 (시스템 구조에 맞춰 조직을 재구성하는 전략)&lt;br&gt;커리어 성장 &lt;br&gt;호기심 유지하기 p524&lt;br&gt;프로젝트를 완수하는 데 필요한 기간을 추정하는 일은 경력이 어느 정도 쌓인 지금도 여전히 어려운 과제입니다. p525&lt;br&gt;과거의 사례 참고하기&lt;br&gt;플래닝 포커&lt;br&gt;필수적인 작업과 미뤄도 되는 작업을 명확히 구분&lt;br&gt;일정 추정에 영향을 주는 주요 요소를 파악하는 것이 중요 , 프로젝트 구성원들의 기술적 성숙도도 고려 &lt;br&gt;과거의 실수를 교훈 삼아 다음번에는 더 나은 결과를 만들어낸다면 그것으로 충분히 잘한 것입니다. p528&lt;br&gt;트러블 슈팅이 끝난 후에는 반드시 복기하는 시간을 갖는 것을 추천합니다. p528&lt;br&gt;독성 말투 쓰지 않기 p528&lt;br&gt;어느 회사도 100% 만족스러운 곳은 없고 나 역시 완벽한 사람이 아니라는 사실 p529&lt;br&gt;인생은 게임이 아닙니다. 커리어 관리 또한 항상 가장 좋은 길만 선택할 수 없습니다. 오히려 높고 낮은 산길을 걷는 여정에 더 가깝습니다. p532&lt;br&gt;누군가의 조언이나 경험이 도움이 될 수는 있지만, 그것은 그 사람의 경험에 한정 된 것입니다. 인생에는 정해진 하나의 길이 없습니다. 스스로를 믿고 때로는 속도를 조절하며 삶이라는 긴 여정을 즐겁게 걸어가길 바랍니다. p532&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;세상은 우리에게 언제나 리더가 되라한다 (유진호 크라우드웍스 서비스 개발실 실장)&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 직업을 갖든 평생 내가 하고 싶은 일만 하는 경우는 거의 없습니다. p533&lt;br&gt;'경력이 쌓일수록, 더 많은 책임을 가져야 한다' p533&lt;br&gt;만약 책임이 늘어나지 않고 있다면 의심해 봐야 할 수 있습니다. '내가 성장을 하고 있는 걸까?'&lt;br&gt;성취예측모형 - 요소중에 어떤 것이 높아서 성취를 할 수 있었는지 스스로 생각해보는 시간이 필요하다. p535&lt;br&gt;시장은 '애써 시간을 내' 현재 시장이 어떤 상황인지 알려줍니다. 긍정적인 피드백이나 부정적인 피드백이나 모두 기업이 여러분을 어떻게 생각하는 지 자세하게 알려준다고 생각하면, 평안한 마음으로 차분하게 정보를 취합할 수 있습니다. p537&lt;br&gt;남들에게 내가 발견될 기회를 늘릴 수 있는 최고의 방법은 '글쓰기' p538&lt;br&gt;성공한 사람은 역경을 유리하게 변화시키는 사람 p541&lt;br&gt;시련이 있어도 이를 세상에 좋은 영향력을 미치겠다는 비전을 세움으로서 역경을 이겨내며 결국 세상을 더 낫게 만들 수 있는 인물이 되시기를, 그 인물이 될 공부를 쉬지 않고 하시는 그런 인생을 살아 가시길 ~ p543&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;변화에 적응하고 실행하는 개발자의 마인드셋 (정현준 만타 프로덕트 리더)&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 항상 변화하는 환경에 적응해야 합니다. p545&lt;br&gt;헤라클레이토스 &quot;우주에 변하지 않는 유일한 것은 '변한다'는 사실 뿐&quot; p545&lt;br&gt;항상 필요한 건 배우고 적응하겠다는 마음가짐. 실행하는 자세 p546&lt;br&gt;&quot;완벽은 좋은 것의 적이다&quot; p548&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI시대, 개발자의 성장과 미래 홍승환(스캐터랩 ML 리드)&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 개발은 '완벽'한 걸 만드는 게 사실상 불가능합니다. 혹시 굉장히 뛰어난 개발 조직이 있어서 '완벽'한 걸 만들 수 있다고 하더라도 비즈니스에서 필요로 하는 시간에 맞춰서 만들지는 못할겁니다. p548&lt;br&gt;개발자는 코드를 넘어서 문제를 정의하고, 그 문제를 해결하는 사람입니다. p554&lt;br&gt;'문제를 정의하는 사람'(개발자)&lt;br&gt;1단계(주니어) : 팀의 주어진 문제를 해결하는 데 집중. 정의가 명확하게 주어진 문제를 해결하는 것이 주요 업무인 경우가 많음&lt;br&gt;2단계(미들): 문제는 정의되어 있지만 해답은 명확하지 않은 상황. 그만큼 큰 실패 또한 경험하는 시기&lt;br&gt;3단계(시니어): 기술 문제가 비즈니스 문제로 확장되는 단계&lt;br&gt;4단계(리드) : 경력이 더 쌓이면 개발자는 비즈니스 문제를 스스로 정의하고 해결 방법을 설계하는 수준에 다다릅니다. p555&lt;br&gt;그냥 기술을 다룰 줄 아는 사람을 넘어서, 그 기술을 어디에 활용해야 하는지도 아는 개발자가 훌륭한 개발자인 것 같습니다. p555&lt;br&gt;AI가 발전한 세상에서의 개발자는 더욱 고차원적인 일에 시간을 투자하게 될 것이다.라고 이야기하는데, 저는 여기에 '더욱 고차원적인 일'이 사람과 가치에 대한 복잡한 문제를 고민하는 일이라고 생각합니다. p555&lt;br&gt;변화에 적응하며 빠르게 배우는 개발자. 새로운 기술이 등장하더라도 그 기술을 빠르게 습득하고 다른 영역과 연결할 수 있는 능력이 정말 중요해질 겁니다. p558&lt;br&gt;&lt;b&gt;매니저가 알 거라 짐작하지 말고 모두 말하자 p.60&lt;/b&gt;&lt;br&gt;&lt;b&gt;입사 초기부터 업무일지/과시용 문서를 작성하자. 매주 일지를 쓰자. 처음 몇 달 동안 매주 배운것과 이해하지 못한 것을 세가지씩 적는다. p127&lt;/b&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;&lt;b&gt;1장. 개발자 커리어의 기본 사항&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;거절하는 법은 빨리 배울수록 더 큰 도움이 되는 진정한 기술이다. p.138&lt;/b&gt;&lt;br&gt;&lt;b&gt;생산성 높은 엔지니어는 스스로 벽에 부딪혔다는 사실을 깨닫고 인정한다. p.140&lt;/b&gt;&lt;br&gt;&lt;b&gt;팀에서 주니어나 신입일수록 선의 통장 잔고가 더 높다. p.155&lt;/b&gt;&lt;br&gt;&lt;b&gt;사소한 질문을 너무 많이 하거나, 다른 사람의 업무에 자주 끼어들면 잔고는 금방 바닥을 보인다.&lt;/b&gt;&lt;br&gt;&lt;b&gt;선의 통장의 잔고를 너무 빨리 소진하지 말자.&lt;/b&gt;&lt;br&gt;&lt;b&gt;장기적으로는 일을 조금 빨리 끝내는 것보다는 좋은 인간관계가 더 중요하다. p.142&lt;/b&gt;&lt;br&gt;&lt;b&gt;깊이 있게 공부하는 또 다른 방법은 매일 접하는 '지루하지만 필요한 것'을 공부하는것 p.177&lt;/b&gt;&lt;br&gt;&lt;b&gt;어떤 하나의 블럽 시스템의 세부 사항을 배우면 그 시스템의 설계에 블럽과 무관하게 일반적으로 적용될 수 있는 유용한 핵심 요소가 있기 때문 p.178&lt;/b&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;&lt;b&gt;2장. 유능한 소프트웨어 개발자&lt;/b&gt; &lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;좋게 봐야 '느리지만 꾸준한' 엔지니어로 인식될 수 있지만 최악의 경우 '꾸준한'은 빠진다. p221&lt;/b&gt;&lt;br&gt;&lt;b&gt;소프트웨어 엔지니어링에 최종 결정이란 없다. p294&lt;/b&gt;&lt;br&gt;&lt;b&gt;기술 변경, 아키텍처 접근 방식, 비즈니스 규칙 추가 등 거의 모든 결정은 나중에 되돌릴 수 있다.&lt;/b&gt;&lt;br&gt;&lt;b&gt;결정을 되돌리기 매우 쉬운 상황이라면 어떤 선택이라도 하는 것이 결정 불가 상태에 빠지는 것 보다 낫다!&lt;/b&gt;&lt;br&gt;&lt;b&gt;결정을 되돌릴 때는 해당 접근 방식이 효과가 없었던 이유를 문서로 작성해 배포하자.&lt;/b&gt;&lt;br&gt;&lt;b&gt;결국, 소프트웨어 시스템은 끊임없이 변화하는 비즈니스 요구사항과 현실 세계에 발맞춰 진화한다.&lt;/b&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;&lt;b&gt;3장. 다재다능한 시니어&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프로젝트의 투입되는 인력과 타임라인, 범위는 모두 연결되어있다. 하나가 바뀌면 적어도 다른 하나가 따라서 바뀌어야 한다. p311&lt;/b&gt;&lt;br&gt;&lt;b&gt;프로젝트에 참여하는 인원이 줄면 범위 또는 타임라인을 변경하는 편이 가장 편하다. p313&lt;/b&gt;&lt;br&gt;&lt;b&gt;범위/타임라인/인력의 관계를 인식하고, 한 구성 요소가 변경되면 다른 구성 요소도 하나 이상 변경되어야 한다는 사실을 알아두면 좋다. p313&lt;/b&gt;&lt;br&gt;&lt;b&gt;프로젝트 팀원이 교체되는 경우 온보딩 기간에 따라 일정 변경 여부가 달라진다. p315&lt;/b&gt;&lt;br&gt;&lt;b&gt;기존 생각의 틀을 벗어난 절충안을 제시하는 걸 두려워 하지 말자. 상부에서 일방적으로 마감일을 정했다면 거부하자. 제약 조건과 함께 할 수 있는 일과 할 수 없는 일을 명확히 하자. p319&lt;/b&gt;&lt;br&gt;&lt;b&gt;로컬 검증 &amp;gt; CI 유효성 검사 &amp;gt; 테스트 환경에서 배포하기 전 자동화 &amp;gt; 테스트 환경 &amp;gt; 테스트 환경 2 &amp;gt; 사전 프로덕션 환경 &amp;gt; 단계적 출시 &amp;gt; 전체 출시 &amp;gt; 출시 후 p326&lt;/b&gt;&lt;br&gt;&lt;b&gt;테크리드는 스스로 필요할 때 주도권을 잡는 것과 모든 엔지니어가 주도권을 갖고 자신감을 가지고 결정을 내릴 환경을 조성하는 것 사이에서 균형을 잡아야 한다.&lt;/b&gt;&lt;br&gt;&lt;b&gt;테크리드의 목표는 팀원들이 항상 리더에게 의지하고 리더의 결정을 따르도록 '리드'하는 것이 아닌, 팀과 프로젝트의 성공을 돕고 사람들이 최대한 효율적으로 일하도록 돕는 것이다. p376&lt;/b&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;&lt;b&gt;4장. 실용주의 테크리드&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;영향력은 사내 정치의 '좋은' 형태다. 그렇기 때문에 팀을 지원하고 소프트웨어 엔지니어로서 커리어를 발전시키고 싶다면 정치적이지 않으면서도 영향력 있는 사람으로 인식되는 편이 도움이 된다. p404&lt;/b&gt;&lt;br&gt;&lt;b&gt;최첨단 기술을 사용하는 '현대적인' 회사에서 일하면 출근하는 것만으로도 최신 정보를 얻을 수 있다. p508&lt;/b&gt;&lt;br&gt;&lt;b&gt;빠르게 변화하는 기술 업계의 특성상 따라잡지 못하면 나만 뒤처지는 고립공포감(FOMO)을 느낄 수 있다. 하지만 뒤처지지 않으려 계속 학습만 하면 번아웃이 찾아올 위험이 있다. p509&lt;/b&gt;&lt;br&gt;&lt;b&gt;때로는 모든 것에서 벗어나 휴식을 취하자. 쉬는 데에 죄책감을 느끼지 말자! p509&lt;/b&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;&lt;b&gt;롤모델로서의 스태프 및 수석엔지니어&lt;/b&gt; &lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;그가 말하는 1만 시간 법칙에서 1만 시간은 '자신의 기량을 향상시킬 목적으로 반복적으로 하는 수련'을 한 시간을 일컫습니다. &amp;lt;의도적 수련&amp;gt; p27&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;실수는 예방하는 것이 아니라 관리하는 것이다.&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;실수 예방은 행동에서 실수로 가는 경로를 차단하려고 합니다. 즉 실수를 저지르지 말라고 요구합니다. 근데, 사실 이것이 불가능에 가깝습니다. p91&lt;br&gt;&lt;b&gt;&quot;실수는 어떻게든 할 수밖에 없다. 대신 그 실수가 나쁜 결과로 되기 전에 일찍 발견하고 빨리 고치면 된다.&quot;는 겁니다. p91&lt;/b&gt;&lt;br&gt;이미 결과가 난 실수에 대해서는 학습을 통해 &quot;다음 행동할 때 이렇게 하자&quot;는 계획을 세우기도 합니다. p91&lt;br&gt;실수 관리 문서화에서는 실수가 나쁜 결과를 내기 전에 빨리 회복하도록 돕고, 실수를 공개하고, 실수에 대해 서로 이야기하고 거기에서 배우는 분위기가 생깁니다. p91&lt;br&gt;&lt;b&gt;실수가 없으면 학습하지 못합니다. p92&lt;/b&gt;&lt;br&gt;&lt;b&gt;이는 학습이론의 기본입니다. 즉, 실수 관리를 하는 문화일수록 학습을 더 잘합니다. p93&lt;/b&gt;&lt;br&gt;신뢰가 깨어져 있는 상태에서는 어떤 행동을 해도 악의적으로 보인다. p101&lt;br&gt;뛰어난 소프트웨어 개발자일수록 타인과 인터렉션에 더 많은 시간을 쓰며 초보 개발자들에게 조언을 할 때 사회적인 측면이 포함됩니다. 기술적인 조언만 하는게 아니라는 뜻 입니다. p102&lt;br&gt;제가 중요하게 다루는 사회적 기술은 도움받기, 피드백 주고받기, 영향력 미치기, 가르치고 배우기, 위임하기 등이 있습니다. p104&lt;br&gt;그 조직원들은 선생님을 좋아하나요? p105&lt;br&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>지수쓰</author>
      <guid isPermaLink="true">https://w97ww.tistory.com/122</guid>
      <comments>https://w97ww.tistory.com/122#entry122comment</comments>
      <pubDate>Wed, 26 Mar 2025 00:38:02 +0900</pubDate>
    </item>
    <item>
      <title>[DB] 커버링 인덱스, 실행 계획 Using index</title>
      <link>https://w97ww.tistory.com/121</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;작년에 작성했던 &lt;/span&gt;&lt;span&gt;&lt;a style=&quot;color: #4183c4;&quot; href=&quot;https://w97ww.tistory.com/104&quot;&gt;&lt;span&gt;인덱스로 조회 시 개선되는 성능 확인해 보기&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;span&gt; 에 이어서, 이번에는 &lt;b&gt;실행계획&lt;/b&gt;을 확인하고, &lt;b&gt;커버링 인덱스&lt;/b&gt;가 사용되는 쿼리는 어떤 것인지 좀 더 정리해보려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;기본 개념 및 특징&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예전에 정리했던 인덱스 개념&lt;br /&gt;&lt;a href=&quot;https://github.com/kang-jisu/dev-study-log/blob/main/background/database/%EC%9D%B8%EB%8D%B1%EC%8A%A4.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/kang-jisu/dev-study-log/blob/main/background/database/%EC%9D%B8%EB%8D%B1%EC%8A%A4.md&lt;/a&gt;&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;인덱스&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;insert, update, delete (Command)의 성능을 희생하고 select(Query)의 성능을 향상시킨다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;데이터 파일이 있는 디스크에 직접 접근하지 않고, 메모리 저장소 내(인덱스) 안에서 최대한 해결하면서 성능을 개선할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;클러스터드 인덱스&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;물리적으로 데이터가 정렬되어 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;insert시에 인덱스의 행을 기준으로 정렬한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;테이블당 하나의 클러스터드 인덱스를 포함한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;설정 방법 ) PK로 설정하거나, clustered index로 추가하기&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;br /&gt;논 클러스터드 인덱스&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;논리적으로 정렬되지 않은 상태로 저장&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;데이터를 직접 가지고 있지 않고 위치를 가리킨다. (클러스터드 인덱스를 가리키거나, 실제 데이터 저장 위치를 가리킴)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;클러스터드 인덱스(정렬된 인덱스)에 의해 저장된다. 클러스터드 인덱스에 비해 검색속도는 느리지만 입력, 수정, 삭제에는 성능이 더 좋을 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;설정 방법 ) nonclustered index 추가, 혹은 기본 인덱스 설정&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;커버링 인덱스&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;실제 데이터의 접근 없이&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt; 원하는 데이터를 &lt;b&gt;인덱스에서만 추출할 수 있는 인덱스&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;디스크 I/O를 통해 데이터 블록을 읽지 않고도 원하는 데이터를 가져올 수 있어 성능을 올릴 수 있다. (클러스터드 인덱스를 사용한다)&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 커버링 인덱스들 이용해서만 조회가 가능하다면 성능을 개선할 수 있을 것이다! 지금 내가 작성하는 쿼리가 커버링 인덱스를 사용할 수 있는지를 확인하는 방법 중 하나가 실행 계획을 이용하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;실행 계획&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/using-explain.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://dev.mysql.com/doc/refman/8.0/en/using-explain.html&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;작성한 쿼리에 대해서 &lt;b&gt;explain&amp;nbsp;&lt;/b&gt;키워드를 사용하면 쿼리의 실행 계획을 확인할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;간단히 말하면 이 작성된 SQL을 DB에서 어떻게 처리하여 가져올 것인지 예상하는 것인데, 몇 개의 / 어떤 종류의 쿼리가 있는지, 데이터를 인덱스로 조회하는지, 풀 스캔으로 조회하는지 등등 확인하면서 실제로 쿼리를 사용해 보기 전에 예측해 보며 쿼리를 최적화할 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;실행 계획의 결과로 여러 필드들이 있는데, 그중 Extra 필드를 보면 인덱스가 활용되고 있는지를 확인할 수 있다. (extra필드에 using index)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;Extra&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Using where&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Range 검색 이후에는 직접 데이터 필드에 접근하여 추출하였음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Using index&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;인덱스만으로 원하는 데이터를 추출하였음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Using index condition&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;인덱스의 칼럼만 사용하여 조건의 일부를 평가할 수 있는 경우&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;mysql 5.6 이상부터 인덱스 조건을 스토리지 엔진에 넘겨서 최대한 필터링&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;5.6 미만에서는 조회조건에 포함되는 필드가 모두 index여야만 가능했는데, 일부만 들어가도 필터링이 된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;출처&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;인덱스 정리 및 팁 &lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://jojoldu.tistory.com/243&quot;&gt;https://jojoldu.tistory.com/243&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Database index, 제대로 알아보기 &lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://gngsn.tistory.com/88&quot;&gt;https://gngsn.tistory.com/88&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Covering index, 성능 테스트 &lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://gngsn.tistory.com/194&quot;&gt;https://gngsn.tistory.com/194&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;MySQL에서 커버링 인덱스로 쿼리 성능을 높여보자!! &lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://gywn.net/2012/04/mysql-covering-index/&quot;&gt;https://gywn.net/2012/04/mysql-covering-index/&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;커버링 인덱스 &lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://icarus8050.tistory.com/44&quot;&gt;https://icarus8050.tistory.com/44&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;예제 &lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x3PDN/btstw4Hid4d/1qxQDGLPhAzBJabus9xhk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x3PDN/btstw4Hid4d/1qxQDGLPhAzBJabus9xhk0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;260&quot; data-filename=&quot;edited_스크린샷 2023-09-11 오전 12.59.10.png&quot; style=&quot;width: 61.0465%; margin-right: 10px;&quot; data-widthpercent=&quot;61.76&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x3PDN/btstw4Hid4d/1qxQDGLPhAzBJabus9xhk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx3PDN%2Fbtstw4Hid4d%2F1qxQDGLPhAzBJabus9xhk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;260&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zXot3/btstzoSfwzn/1fTCqftEV1MuUhYQhN7Lg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zXot3/btstzoSfwzn/1fTCqftEV1MuUhYQhN7Lg1/img.png&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;252&quot; data-is-animation=&quot;false&quot; data-filename=&quot;edited_스크린샷 2023-09-11 오전 1.10.06.png&quot; data-widthpercent=&quot;38.24&quot; style=&quot;width: 37.7907%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zXot3/btstzoSfwzn/1fTCqftEV1MuUhYQhN7Lg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzXot3%2FbtstzoSfwzn%2F1fTCqftEV1MuUhYQhN7Lg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;252&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임 참가자 테이블에서, 특정 챔피언별로 각각 최근 5건의 게임기록을 조회하려는 쿼리를 짜보려고 한다. ( 예제로 만들어볼 만한 적당한 요구사항이 없어서 대충 지어내보았다., )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-11 오전 1.00.09.png&quot; data-origin-width=&quot;1960&quot; data-origin-height=&quot;252&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnwMAW/btstwE28Oga/QKSGk2KbWbl53BPee8bE3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnwMAW/btstwE28Oga/QKSGk2KbWbl53BPee8bE3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnwMAW/btstwE28Oga/QKSGk2KbWbl53BPee8bE3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnwMAW%2FbtstwE28Oga%2FQKSGk2KbWbl53BPee8bE3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1960&quot; height=&quot;252&quot; data-filename=&quot;스크린샷 2023-09-11 오전 1.00.09.png&quot; data-origin-width=&quot;1960&quot; data-origin-height=&quot;252&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무런 설정을 안 한 상태에서는 PK인 (game_id, team_id, participant_id)만 index로 설정이 되어있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 챔피언, 시간 순 정렬 조회를 하기 위해서는 champion_id와 created로 where절에 서브쿼리를 넣어 작성해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694361774151&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT *
FROM game_record_participants as ori
WHERE 
(
    SELECT count(*) 
	FROM game_record_participants as sub
	WHERE sub.champion_id = ori.champion_id
		and sub.game_id &amp;lt;= ori.game_id
) &amp;lt;= 5
;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1) 서브쿼리에 인덱스 없이 조회&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-11 오전 1.07.59.png&quot; data-origin-width=&quot;1382&quot; data-origin-height=&quot;118&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZEZqe/btstwY74W5W/TfpNkyvq5DLk2GWEgcd0C1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZEZqe/btstwY74W5W/TfpNkyvq5DLk2GWEgcd0C1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZEZqe/btstwY74W5W/TfpNkyvq5DLk2GWEgcd0C1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZEZqe%2FbtstwY74W5W%2FTfpNkyvq5DLk2GWEgcd0C1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1382&quot; height=&quot;118&quot; data-filename=&quot;스크린샷 2023-09-11 오전 1.07.59.png&quot; data-origin-width=&quot;1382&quot; data-origin-height=&quot;118&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;explain으로 실행계획을 조회하면 id=2인 쿼리의 Extra가 Using where이다. rows를 똑같이 11850(전체 rows 수)만큼 조회하고 있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-11 오전 1.09.57.png&quot; data-origin-width=&quot;930&quot; data-origin-height=&quot;74&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dMW8cE/btstwH6DRRo/tXoCKkq7McJPSuub55ekJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dMW8cE/btstwH6DRRo/tXoCKkq7McJPSuub55ekJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dMW8cE/btstwH6DRRo/tXoCKkq7McJPSuub55ekJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdMW8cE%2FbtstwH6DRRo%2FtXoCKkq7McJPSuub55ekJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;930&quot; height=&quot;74&quot; data-filename=&quot;스크린샷 2023-09-11 오전 1.09.57.png&quot; data-origin-width=&quot;930&quot; data-origin-height=&quot;74&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 실제로 쿼리를 수행히보면 10000개의 rows에서 770개의 rows를 가져오기 위해서 90 sec나 걸린 것을 볼 수 있다. ㄷ_ㄷ&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2) 서브쿼리에 사용되는 칼럼을 인덱스로 만들어서 조회&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1694362350149&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE INDEX ix_query ON game_record_participants (champion_id,created);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-11 오전 1.13.06.png&quot; data-origin-width=&quot;1932&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLg4Hg/btstx6xOjma/dKRtqwVDfCgknOKKmvHCF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLg4Hg/btstx6xOjma/dKRtqwVDfCgknOKKmvHCF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLg4Hg/btstx6xOjma/dKRtqwVDfCgknOKKmvHCF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLg4Hg%2Fbtstx6xOjma%2FdKRtqwVDfCgknOKKmvHCF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1932&quot; height=&quot;240&quot; data-filename=&quot;스크린샷 2023-09-11 오전 1.13.06.png&quot; data-origin-width=&quot;1932&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;사용되는 칼럼인 champion_id와 created를 인덱스로 만들어줬다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-11 오전 1.13.24.png&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;70&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mM7lb/btstyjKpk5X/cnvA3COm1I4ycBnuywMz4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mM7lb/btstyjKpk5X/cnvA3COm1I4ycBnuywMz4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mM7lb/btstyjKpk5X/cnvA3COm1I4ycBnuywMz4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmM7lb%2FbtstyjKpk5X%2FcnvA3COm1I4ycBnuywMz4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;898&quot; height=&quot;70&quot; data-filename=&quot;스크린샷 2023-09-11 오전 1.13.24.png&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;70&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;position: absolute;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-11 오전 1.13.33.png&quot; data-origin-width=&quot;1824&quot; data-origin-height=&quot;144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/be4ZV4/btstxid76Us/UqMXAHTd8lVLiiEpZ5GyrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/be4ZV4/btstxid76Us/UqMXAHTd8lVLiiEpZ5GyrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/be4ZV4/btstxid76Us/UqMXAHTd8lVLiiEpZ5GyrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbe4ZV4%2Fbtstxid76Us%2FUqMXAHTd8lVLiiEpZ5GyrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1824&quot; height=&quot;144&quot; data-filename=&quot;스크린샷 2023-09-11 오전 1.13.33.png&quot; data-origin-width=&quot;1824&quot; data-origin-height=&quot;144&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;똑같이 쿼리를 실행했더니 0.5 sec 만에 실행이 되었고 Extra에 Using index가 쓰였으며 rows도 75개만 가지고 왔다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;생각보다 큰 성능 차이가 있는 것 같다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;DB를 조회하는 쿼리를 작성할 때 그냥 단순히 동작만 하도록 쿼리를 짜곤 했는데, 작성한 쿼리에 대해서 실행계획으로 확인해 보고 만들어 있는 PK나 인덱스를 활용해 쿼리가 작성되고 있는지 확인한다면 조금 더 성능을 고려해서 만들 수 있을 것 같다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;아직 너무 모르는 게 많아서 mysql에 대해서 좀 더 공부해서 개념을 보완해야겠다. T_T &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Backend/개발</category>
      <category>Index</category>
      <category>MySQL</category>
      <author>지수쓰</author>
      <guid isPermaLink="true">https://w97ww.tistory.com/121</guid>
      <comments>https://w97ww.tistory.com/121#entry121comment</comments>
      <pubDate>Mon, 11 Sep 2023 01:25:40 +0900</pubDate>
    </item>
    <item>
      <title>[Kotlin] Kotest, mockk</title>
      <link>https://w97ww.tistory.com/120</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;코틀린 스타일 테스트코드&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;기존에 사용하던 Junit, AssertJ, Mockito를 이용해서도 테스트코드를 작성할 수 있지만, 중괄호를 활용한 코틀린 dsl 스타일을 적용하기 쉽지 않은데&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;kotest&lt;/b&gt;와&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;mockk&lt;/b&gt;(코틀린에서 사용하는 mocking라이브러리)를 이용하면 Mocking이나 assertion 과정에서 Kotlin dsl이나 infix를 사용해 조금 더 코틀린스럽게 테스트코드를 작성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Kotest&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코틀린 진영에서 가장 많이 사용되는 테스트 프레임워크&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코틀린 DSL을 활용해 테스트코드를 작성할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다양한 테스트 레이아웃(StringSpec, FunSpec, BehaviorSpec 등 ) 제공&lt;/li&gt;
&lt;li&gt;Kotlin DSL 스타일의 Assertion 기능 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;# kotlin dsl이란
Domain Specific Language : 특정 도메인에 국한해 사용하는 언어

범용 프로그래밍 언어인 C, C++ 등을 가지고 알고리즘을 푼다.
SQL언어는 DB (특정영역, 도메인)에 초점을 맞추고 기능을 제한

특징
- 선언적 문법 ( &amp;lt;-&amp;gt; 명령적 )
- 깔끔한 코드작성이 가능하다.
  ㄴ 해당 코드가 어떤 로직을 가지고 작업할지 명확하게 이해할 수 있다.

kotlin dsl : https://w97ww.tistory.com/119&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Kotest Testing Styles&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://kotest.io/docs/framework/testing-styles.html#free-spec&quot;&gt;https://kotest.io/docs/framework/testing-styles.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;다양한 Spec이 존재해서 상황에 따라 사용하면 될 듯하다.&lt;/li&gt;
&lt;li&gt;나는 보통 가볍게 테스트할 때 FunSpec, BehaviorSpec(BDD형태) 를 많이 쓴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 이런 모양을 가지고 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1691977047993&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Tests: XxxSpec({
	keyword(&quot;테스트코드 설명&quot;) {
      // keyword는 Spec에 따라서 
      // test, &quot;string&quot;, should, describe, given, when, then 등 다양하다.
    }

	xkeyword(&quot;테스트 비활성화&quot;){
      // keyword앞에 x가 붙으면 비활성화가 된다.
    }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;sample 1) FunSpec&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1691976804653&quot; class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MyTests : FunSpec({
    test(&quot;addCalculator - 두 파라미터를 더한 값이 반환 된다.&quot;) {
    	val result = addCalculator(1, 2)
        result shouldBe 3
    }
})


fun addCalculator(a: Int, b: Int): Int = a + b&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;test 부분에 테스트의 설명을 적고, 괄호 안 람다 식에 필요한 assertion문을 넣어 테스트를 하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 의존성 주입이 필요 없는 도메인 클래스나 도메인 서비스 메소드를 테스트할 때 많이 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;sample 2) BehaviorSpec&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1691977132245&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MyTests : BehaviorSpec({
    given(&quot;a broomstick&quot;) { // Given 도 가능 
        and(&quot;a witch&quot;) { // And
            `when`(&quot;The witch sits on it&quot;) { // When
                and(&quot;she laughs hysterically&quot;) { // And
                    then(&quot;She should be able to fly&quot;) { // Then
                        // test code
                    }
                }
            }
        }
    }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BDD 스타일의 테스트코드를 작성할 때 유용하다. `when`이 예약어라서 사용할 때 \`when`으로 써주어야 해서, 키워드를 대문자로 사용하는 게 편할 수도 있다. And를 이용해서 이어 쓸 수 있고 xGiven, xwhen처럼 x를 붙이면 비활성화할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;sample 3) AnnotationSpec&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1691977292008&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class AnnotationSpecExample : AnnotationSpec() {

    @BeforeEach
    fun beforeTest() {
        println(&quot;Before each test&quot;)
    }

    @Test
    fun test1() {
        1 shouldBe 1
    }

    @Test
    fun test2() {
        3 shouldBe 3
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Junit 스타일의 어노테이션 방식을 똑같이 사용할 수 있는 AnnotationSpec도 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;수명주기 / 전후 처리&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://kotest.io/docs/framework/lifecycle-hooks.html#overriding-callback-functions-in-a-spec&quot;&gt;https://kotest.io/docs/framework/lifecycle-hooks.html#overriding-callback-functions-in-a-spec&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BehaviorSpec같은 중첩 테스트에서는 테스트 수명주기를 이해하고 사용하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Given, When, Then을 만들다 보면 테스트가 중첩으로 생성되기 때문에, 이 수명주기에 따라서 전역으로 관리하거나 mockk 객체를 처리하면 스코프 안에서 얼마나 반복되어 실행되는지, 모킹의 범위가 얼마인지, 어느 시점에 mockk 객체를 초기화해야 될지 등을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;beforeContainer&lt;/code&gt;, &lt;code&gt;afterContainer&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Given으로 묶이는 그 아래까지 포함한 영역&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;beforeEach&lt;/code&gt;, &lt;code&gt;afterEach&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Then영역&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;beforeTest&lt;/code&gt;, &lt;code&gt;afterTest&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;beforeAny,&lt;/code&gt; &lt;code&gt;afterAny&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;beforeSpec&lt;/code&gt;, &lt;code&gt;afterSpec&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트 영역 전체를 Spec이라고 한다&lt;/li&gt;
&lt;li&gt;XxxSpec을 구현하는 &lt;code&gt;({})&lt;/code&gt; 영역&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;등..&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Assertion&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://kotest.io/docs/assertions/assertions.html&quot;&gt;https://kotest.io/docs/assertions/assertions.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;보통 &lt;code&gt;shouldXX&lt;/code&gt; 형태의 assertion 메소드를 이용해서 검증한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;io.kotest.matchers&lt;/code&gt; 패키지에 종류별로 아주 많이 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_스크린샷 2023-09-03 오후 5.14.35.png&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wZU8q/btssUJpmMMm/omK0VKnv703rAKbx4A05KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wZU8q/btssUJpmMMm/omK0VKnv703rAKbx4A05KK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wZU8q/btssUJpmMMm/omK0VKnv703rAKbx4A05KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwZU8q%2FbtssUJpmMMm%2FomK0VKnv703rAKbx4A05KK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;360&quot; data-filename=&quot;edited_스크린샷 2023-09-03 오후 5.14.35.png&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;shouldBe, shouldBeNot&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;shouldHaveSize, shouldContain, ShouldBeGreaterThan ,,&lt;/li&gt;
&lt;li&gt;shouldThrow&amp;lt;&amp;gt;&lt;/li&gt;
&lt;li&gt;등등&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딱히 정해진건 없으니 패키지를 찾아보거나 많이 사용해 보면서 적절한 assertions을 쓰면 될 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 &lt;code&gt;list.size() shouldBe 2&lt;/code&gt; 보단 &lt;code&gt;list shouldHaveSize 2&lt;/code&gt; 를 쓴다거나, 리스트의 아이템을 직접 조회하며 assert문을 쓰지 않고&amp;nbsp;&lt;code&gt;shouldBeSortedWith(Comparator)&lt;/code&gt; 를 쓴다거나,,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외처리에 대한 테스트를 할 때 &lt;code&gt;shouldThrow&lt;/code&gt;로 검증을 할 수도 있고,&amp;nbsp;&lt;code&gt;runCatching&lt;/code&gt;으로 예외를 잡아서 그 결괏값을 다른 assert문으로 검증하는 것 도 괜찮은 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Mockk&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;kotests와 같이 쓸 수 있는 유용한 모킹 라이브러리이다. jUnit을 사용할 때는 mockito를 주로 썼다면, 코틀린을 쓸 때는 mockk를 쓰면 보통 쓰는 편이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거의 비슷해서 각자 장/단점을 비교해서 취향껏 선택할 수 있을 정도의 차이인 것 같지만, kotlin dsl 형태로 되어있는 것이나 좀 더 코틀린스러운 테스트코드를 짤 수 있는 느낌이다. 더 자세한 특징은.. 생략 ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 객체 생성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;val aService = mockk&amp;lt;AService&amp;gt;()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;val aService: AService = mockk()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. Matcher 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 링크 : &lt;a href=&quot;https://mockk.io/#matchers&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://mockk.io/#matchers&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1693730346170&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;every { aService.something() } returns SomeObject()

every { aService.something() } throws Exception()

justRuns { aService.something() } // 결과값이 없는 메소드 단순 호출만 mocking&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 검증&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검증은 보통 kotest의 assertion으로 하거나, &lt;code&gt;verify&lt;/code&gt; 메소드를 사용해서 mocking 한 클래스의 특정 메소드가 호출되었는지 동작만을 검증한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이때 shouldBe 같은 assert문이 아닌 verify로 메서드가 호출되는지를 검증할 때, 모든 테스트에 걸쳐서 호출 횟수를 세기 때문에 테스트코드의 호출 순서에 따라서 verify문이 실패할 수 있다. (테스트끼리 독립적이지 않다)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 테스트마다 mock을 clear 하고 다시 mocking을 해줘야 하는데 beforeEach, afterTest 등을 이용해 &lt;b&gt;수명주기에 따라서 해당 동작을 전역적으로 관리할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 부분 때문에 kotest의 수명주기를 잘 알아두어야 테스트코드를 더 잘 작성할 수 있을 듯하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;mock vs stub&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Test Double&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;테스트&amp;nbsp;목적으로&amp;nbsp;실제&amp;nbsp;객체&amp;nbsp;대신&amp;nbsp;사용되는&amp;nbsp;모든&amp;nbsp;종류의&amp;nbsp;가상&amp;nbsp;객체&lt;br /&gt;&lt;br /&gt;&lt;b&gt;mock vs stub&lt;/b&gt;&lt;br /&gt;아주 살짝 비교를 해본다면&amp;nbsp;&lt;br /&gt;- stub은 실제로 인스턴스화하여 실제로 동작하는 것처럼 만드는 객체&lt;br /&gt;- mock은 호출에 대한 기대를 명세하고, 그 내용에 따라 동작하도록 프로그래밍된 객체.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상태 검증 vs 행위 검증&lt;/b&gt;&lt;br /&gt;mock과 stub을 비교할 때 상태 검증, 행위 검증이라는 말을 쓰는데 stub은 메서드가 수행된 뒤에 결과 값의 &lt;b&gt;상태&lt;/b&gt;를 확인한다면, mock은&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 동작을 수행했는지 행위만 검증한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예제 코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1693730628053&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class AServiceTests : BehaviorSpec({
    val dBService: DBService = mockk()
    

    val aService = AService(
            dBService
    )

    Given(&quot;id가 주어지고&quot;) {
        val id = &quot;1&quot;
        When(&quot;data가 존재하면&quot;) {
            every { dBService.get(id)} returns Data(&quot;1&quot;, &quot;content&quot;)
            aService.request(RequestDto(&quot;1&quot;, &quot;new content&quot;))

            Then(&quot;modify가 호출된다.&quot;) {
                verify(exactly = 1) { dBService.modify(any()) }
                verify(exactly = 0) { dBService.save(any()) }
            }
        }

        When(&quot;data가 존재하지 않으면&quot;) {
            every { dBService.get(id)} returns null
            aService.request(RequestDto(&quot;1&quot;, &quot;content&quot;))

            Then(&quot;save가 호출된다.&quot;) {
                verify(exactly = 0) { dBService.modify(any()) }
                verify(exactly = 1) { dBService.save(any()) }
            }
        }
    }

    // 전역적으로 clear, mocking할 만한 메소드들은 수명주기 메소드와 함께 설정
    beforeTest{
        justRun { dBService.modify(any())}
        justRun { dBService.save(any()) }
    }

    afterTest {
        clearMocks(dBService)
    }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘에 헥사고날 패턴의 장점을 느낀 건 특정 port를 mocking 해두고 호출되었는지 정도만 검증하면 다른 클래스에 독립적이게 원하는 기능만 테스트할 수 있어서 좋았던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고할 링크&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=PqA6zbhBVZc&amp;amp;t=519s&quot;&gt;https://www.youtube.com/watch?v=PqA6zbhBVZc&amp;amp;t=519s&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kotest &lt;a href=&quot;https://techblog.woowahan.com/5825/&quot;&gt;https://techblog.woowahan.com/5825/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;수명주기 관련 &lt;a href=&quot;https://velog.io/@effirin/Kotest%EC%99%80-BDD-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0-%EC%A0%84-%EC%9D%BD%EC%96%B4%EB%91%90%EB%A9%B4-%EC%A2%8B%EC%9D%84-%EC%9E%90%EB%A3%8C%EB%93%A4&quot;&gt;https://velog.io/@effirin/Kotest%EC%99%80-BDD-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0-%EC%A0%84-%EC%9D%BD%EC%96%B4%EB%91%90%EB%A9%B4-%EC%A2%8B%EC%9D%84-%EC%9E%90%EB%A3%8C%EB%93%A4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;mock vs stub &lt;a href=&quot;https://azderica.github.io/00-test-mock-and-stub/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://azderica.github.io/00-test-mock-and-stub/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend/공부,개념</category>
      <category>kotest</category>
      <category>Kotlin</category>
      <category>SpringBoot</category>
      <author>지수쓰</author>
      <guid isPermaLink="true">https://w97ww.tistory.com/120</guid>
      <comments>https://w97ww.tistory.com/120#entry120comment</comments>
      <pubDate>Sun, 3 Sep 2023 17:50:03 +0900</pubDate>
    </item>
    <item>
      <title>[Kotlin] DSL (Domain Specific Language) 이란</title>
      <link>https://w97ww.tistory.com/119</link>
      <description>&lt;h1 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;b&gt;&lt;span&gt;DSL&lt;/span&gt;&lt;/b&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Domain Specific Language&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;특정 과업, 영역에 초점을 맞추고 그 영역에 필요하지 않은 기능을 없앤 영역 특화 언어&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;ex) SQL, 정규식&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;SQL 문장을 실행할 때 클래스나 함수를 선언하는 것부터 시작하지 않고, 모든 SQL 문장은 첫 키워드가 수행하려는 연산의 종류를 지정하고, 각 연산은 처리해야 할 작업에 맞춰 각각 서로 다른 문법과 키워드를 사용한다. &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;&amp;lt;비교&amp;gt; 범용 프로그래밍 언어&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;컴퓨터로 풀 수 있는 모든 문제를 충분히 풀수있는 기능을 제공&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;ex) C, C++ 언어&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;&lt;/div&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;DSL의 특징&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;선언적&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;선언적이다 &lt;/span&gt;&lt;span&gt;&lt;code&gt;&lt;span&gt;&amp;lt;-&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/span&gt;&lt;span&gt; 명령적(범용 프로그래밍 언어)&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;어떤 연산을 완수하기 위해 필요한 각 단계를 순서대로 정확히 기술하는 명령적 언어와는 달리, &lt;/span&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;선언적 언어는 원하는 결과를 기술하기만 하고 세부 실행은 언어를 해석하는 엔진에 맡긴다.&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;실행 엔진이 결과를 얻는 과정을 최적화 하기 때문에 선언적 언어가 더 효율적인 경우가 자주 있다. &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;단점&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;DSL을 범용 언어로 만든 호스트 애플리케이션과 함께 조합하기가 어렵다. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;DSL 자체의 문법을 가지고 작성한 프로그램을 다른 언어에서 호출하려면, DSL 프로그램을 별도의 파일이나 문자열 리터럴로 저장해야 한다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;컴파일 시점 검증이 어려움&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;디버깅이나 IDE 지원을 받기 어려움&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;호스트 애플리케이션의 범용언어와 DSL 모두 익혀야함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;&lt;/div&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;내부 DSL&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 DSL의 단점을 해결하며, DSL의 다른 이점을 살리는 방법으로 내부 DSL이라는 개념이 나왔다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;내부 DSL이란 범용 언어로 작성된 프로그램의 일부이며, 범용 언어와 동일한 문법을 사용한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;DSL의 핵심 장점을 유지하며 주 언어를 특별한 방법으로 사용하는 것&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;예제 [ 외부 DSL vs 내부 DSL ]&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;가장 많은 고객(Customer)이 살고 있는 나라(Country)를 알아내는 것&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;외부 DSL (SQL)로 작성한 코드&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;SELECT Country.name, COUNT(Customer.id)
 FROM Country
 JOIN Customer
 &amp;nbsp;ON Country.id = Customer.country_id
GROUP BY Country.name
ORDER BY COUNT (Customer.id) DESC
 &amp;nbsp;LIMIT 1&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;내부 DSL (코틀린 Exposed)로 작성한 코드&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;(Country join Customer)
  .slice(Country.name, Count(Customer.id))
  .selectAll()
  .groupBy(Country.name)
  .orderBy(Count(Customer.id), isAsc = true)
  .limit&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;실행하면 동일한 쿼리가 생성, 실행되지만, 작성할 때는 내부 DSL로 작성한 코드는 코틀린 코드이며, 코틀린 메서드이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;또한 쿼리를 실행한 결과가 네이티브 코틀린이기 때문에 SQL 질의가 돌려주는 결과를 코틀린 코드로 반환할 필요가 없다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이러한 것을 내부 DSL이라고 부른다. 코드는 어떤 구체적인 과업을 달성하기 위한 것이지만, 범용 언어의 라이브러리로 구현된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;출처&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;코틀린 인 액션 11장 - DSL 만들기&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;&lt;/div&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;코틀린 DSL의 예로 , Kotest나 Gradle(Groovy dsl, kotlin dsl)을 들 수 있다. &lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;(참고) Kotest는 DSL을 지원하는 코틀린으로 만든 테스트 프레임워크이다. &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;둘 다 코틀린 문법으로 만들어진 DSL이고, 타입에 안전하고 IDE의 지원을 받을 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;코틀린을 이용해 간단하게 사용할 수 있고, 코드의 가독성이 올라간다. 하지만 내부적으로 어떤 동작을 하는지는 파악하기 어렵다. (코틀린 코드로 직접 그 테스트를 작성하거나 빌드를 하기 위해 코드를 짜는 게 아니라 내부의 실행 엔진에 의해 동작되기 때문)&lt;/span&gt;&lt;/p&gt;</description>
      <category>Backend/공부,개념</category>
      <author>지수쓰</author>
      <guid isPermaLink="true">https://w97ww.tistory.com/119</guid>
      <comments>https://w97ww.tistory.com/119#entry119comment</comments>
      <pubDate>Tue, 18 Jul 2023 23:41:21 +0900</pubDate>
    </item>
    <item>
      <title>[Monitoring] APM, Spring Boot Actuator, micrometer</title>
      <link>https://w97ww.tistory.com/118</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;서버 모니터링에 사용되는 기술/개념들을 하나씩 찍먹 해보아야겠다. 다른 블로그나 공식 문서에 아주 잘 나와있어서 나는 대략 개념만 이해할 정도로 적으려고 한다 @_ @&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;APM&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;(Application Performance Management/Monitoring)&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;:&amp;nbsp; &lt;/span&gt;&lt;span&gt;애플리케이션 성능 관리 / 모니터링&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;어느 구간에서, 어느 사용자가, 어떤 애플리케이션 사용 중 어느 과정에서 문제가 생긴 것인지를 신속하게 탐지하는 것이 중요함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;APM 구성&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;APM은 Agent / Repository Server / Application Server로 구성됨&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Agent : 웹 서버에 설치되어 데이터를 수집한 뒤&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Repository Server : Agent에서 데이터를 전송하거나 Agent서버로 pull하여 데이터를 받아감&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Application Server : Repository Server에 보관된 데이터를 불러와 애플리케이션 성능을 분석하여 결과물을 사용자에게 제공 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;APM 종류&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;DB 성능 관리&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;SQL의 튜닝 및 DBMS 소프트웨어 병목 현상 모니터링&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;애플리케이션 성능 관리&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt; -&amp;gt; 보통 웹 서버의 APM은 이것을 말함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;E2E(End to End, 최종 사용자) 성능 관리&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;출처&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;APM &lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://m.blog.naver.com/shakey7/221913007126&quot;&gt;https://m.blog.naver.com/shakey7/221913007126&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;Actuator&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;code&gt;Spring Boot Acutator&lt;/code&gt;는 애플리케이션의 모니터링 및 관리에 도움이 되는 기능을 제공하며, &lt;/span&gt;&lt;code&gt;&lt;span&gt;spring-boot-starter-actuator&lt;/span&gt;&lt;/code&gt;&lt;span&gt; 패키지를 추가하여 사용할 수 있다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Spring의 자원 상태를 Http Endpoint 혹은 JMX 형태로 제공한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;가장 기본적인 health check 정보부터 제공되는 API(mappings), 환경변수(env), metrics 등을 제공해 준다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-05-24 오전 2.54.35.png&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhuZ5x/btshaqIdcum/Pgyt2GkggbOgaWry27sj8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhuZ5x/btshaqIdcum/Pgyt2GkggbOgaWry27sj8k/img.png&quot; data-alt=&quot;intellij에서 애플리케이션을 실행시켰을 때 Run 창에서 console옆에 Acutator를 볼 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhuZ5x/btshaqIdcum/Pgyt2GkggbOgaWry27sj8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhuZ5x%2FbtshaqIdcum%2FPgyt2GkggbOgaWry27sj8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;315&quot; data-filename=&quot;스크린샷 2023-05-24 오전 2.54.35.png&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;680&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;intellij에서 애플리케이션을 실행시켰을 때 Run 창에서 console옆에 Acutator를 볼 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;Endpoints&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Acutator의 대부분의 Endpoint는 비활성화된 상태로 제공된다. (부트 2.x버전부터) &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기본적으로 사용 가능한 엔드포인트는 &lt;/span&gt;&lt;code&gt;&lt;span&gt;/health&lt;/span&gt;&lt;/code&gt;&lt;span&gt;와 &lt;/span&gt;&lt;code&gt;&lt;span&gt;/info&lt;/span&gt;&lt;/code&gt;&lt;span&gt;이며, WEB API를 통해 제공받고 싶다면 나머지는 다음 설정으로 활성화할 수 있다. &lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;stylus&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;management.endpoints.web.exposure.include=&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;application.yml 설정&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;application.yml파일의 &lt;code&gt;managment&lt;/code&gt; 하위에 작성하면 여러 기능들을 설정할 수 있다. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;기본적으로 엔드포인트는 &lt;/span&gt;&lt;code&gt;&lt;span&gt;/actuator&lt;/span&gt;&lt;/code&gt;&lt;span&gt; 경로 아래에 제공되며 서버의 port와 address를 사용한다. &lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;이를 변경하기 위해서는 &lt;/span&gt;&lt;code&gt;&lt;span&gt;management.endpoints.web&lt;/span&gt;&lt;/code&gt;&lt;span&gt;의 &lt;/span&gt;&lt;code&gt;&lt;span&gt;base-path&lt;/span&gt;&lt;/code&gt;&lt;span&gt;, &lt;/span&gt;&lt;code&gt;&lt;span&gt;port&lt;/span&gt;&lt;/code&gt;&lt;span&gt;, &lt;code&gt;address&lt;/code&gt;등을 변경하면 된다. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;실제 서버와 다른 포트를 사용하는 것이 보안상 안전할 것 같다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그리고 엔드포인트로 서버의 API나 각종 지표를 쉽게 얻을 수 있기 때문에, 보안상 취약할 수 있다. 설정을 통해서 최대한 비활성화해둔 뒤 필요한 정보들만 활성화해서 내보내는 것이 좋을 것 같다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;아래 참고할만한 링크 중 &lt;/span&gt;&lt;span&gt;&lt;a style=&quot;color: #4183c4;&quot; href=&quot;https://techblog.woowahan.com/9232/&quot;&gt;&lt;span&gt;Actuator 안전하게 사용하기&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;span&gt;에 나온 Spring Security 설정 / properties 설정 코드이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;less&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;// WebSecurityConfig 샘플
protected void configure(HttpSecurity http) throws Exception {
 &amp;nbsp; &amp;nbsp;http
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  .authorizeRequests() // 접근에 대한 인증 설정
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;  .antMatchers(&quot;/login&quot;, &quot;/signup&quot;, &quot;/user&quot;).permitAll() // 누구나 접근 허용
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;  .antMatchers(&quot;/actuator/**&quot;).hasRole(&quot;ADMIN&quot;) // ADMIN만 접근 가능
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;  .anyRequest().authenticated() // 나머지 요청들은 권한의 종류에 상관 없이 권한이 있어야 접근 가능
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  .and()
 &amp;nbsp; &amp;nbsp;  ...
 &amp;nbsp;  ;
  }&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;# Actuator 보안 설정 샘플
​
# 1. Endpoint all disable
management.endpoints.enabled-by-default = false
​
# 2. Enable specific endpoints
management.endpoint.info.enabled = true
management.endpoint.health.enabled = true
​
# 3. Exclude all endpoint for JMX and Expose specific endpoints
management.endpoints.jmx.exposure.exclude = *
management.endpoints.web.exposure.include = info, health
​
# 4. Use other port for Actuator
management.server.port = [포트번호]
​
# 5. Change Actuator Default path
management.endpoints.web.base-path = [/변경된 경로]&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;br /&gt;이외 사용하는 방법이나 디테일한 것들은 공식 문서를 참고하면 될 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;참고할만한 링크&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;a style=&quot;color: #4183c4;&quot; href=&quot;https://techblog.woowahan.com/9232/&quot;&gt;&lt;span&gt;우아한기술블로그-Actuator 안전하게 사용하기&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a style=&quot;color: #4183c4;&quot; href=&quot;https://www.baeldung.com/spring-boot-actuators&quot;&gt;&lt;span&gt;baeldung-spring-boot-acutators&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a style=&quot;color: #4183c4;&quot; href=&quot;https://docs.spring.io/spring-boot/docs/3.0.5/reference/html/actuator.html#actuator&quot;&gt;&lt;span&gt;docs.spring.io actuators&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;공식문서 번역&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;a style=&quot;color: #4183c4;&quot; href=&quot;https://godekdls.github.io/Spring%20Boot/spring-boot-actuator/&quot;&gt;&lt;span&gt;토리맘 한글라이즈&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a style=&quot;color: #4183c4;&quot; href=&quot;https://www.devkuma.com/docs/spring-boot/actuator/&quot;&gt;&lt;span&gt;devkuma Spring Boot Acutator&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;Micrometer&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이렇게 Spring Acutator에서 제공해주는 지표들을 모니터링 서비스에 적용시켜야 한다. micrometer는 JVM 기반의 다양한 모니터링 시스템과 호환되는 추상화된 표준 인터페이스를 제공하는 라이브러리이다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Spring Boot Actuator는 Micrometer를 의존성으로 가지고 있고, micrometer의 &lt;/span&gt;&lt;span&gt;&lt;code&gt;&lt;span&gt;MeterRegistry&lt;/span&gt;&lt;/code&gt;&lt;/span&gt;&lt;span&gt; 빈을 자동으로 주입한다. &lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;모듈&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;code&gt;&lt;span&gt;micrometer-core&lt;/span&gt;&lt;/code&gt;&lt;/span&gt;&lt;span&gt; : 최소한의 종속성을 가짐&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;code&gt;&lt;span&gt;micrometer-registry-xx&lt;/span&gt;&lt;/code&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;ex) &lt;/span&gt;&lt;span&gt;&lt;code&gt;&lt;span&gt;micrometer-registry-prometheus&lt;/span&gt;&lt;/code&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;특정 서비스는 registry의존성을 추가해서 사용&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;각각 데이터 수집방식(인터페이스)이 다른데 &lt;/span&gt;&lt;span&gt;&lt;code&gt;&lt;span&gt;micrometer&lt;/span&gt;&lt;/code&gt;&lt;/span&gt;&lt;span&gt;가 파사드처럼 추상화해서 표준 프로토콜같은걸 제공해주는 듯&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;code&gt;&lt;span&gt;micrometer-core&lt;/span&gt;&lt;/code&gt;&lt;/span&gt;&lt;span&gt;는 기본이고 &lt;/span&gt;&lt;span&gt;&lt;code&gt;&lt;span&gt;micromter-registry-{특정서비스}&lt;/span&gt;&lt;/code&gt;&lt;/span&gt;&lt;span&gt; 이렇게 의존성 추가해서 쓰는 듯 하다 &lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;공식 문서&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://micrometer.io/docs/concepts&quot;&gt;https://micrometer.io/docs/concepts&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Backend/공부,개념</category>
      <category>actuator</category>
      <category>Micrometer</category>
      <category>monitoring</category>
      <category>spring boot</category>
      <author>지수쓰</author>
      <guid isPermaLink="true">https://w97ww.tistory.com/118</guid>
      <comments>https://w97ww.tistory.com/118#entry118comment</comments>
      <pubDate>Wed, 24 May 2023 03:18:30 +0900</pubDate>
    </item>
    <item>
      <title>Local Cache, Rate Limiter</title>
      <link>https://w97ww.tistory.com/117</link>
      <description>&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;Local Cache vs Global Cache&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Local Cache&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;서버마다 캐시 따로 저장&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;로컬 서버 장비의 리소스 이용 (Memory, Disk)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;서버 내에서 작동하므로 속도가 빠름 (네트워크를 타지 않음)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;코드 내에서 Map에 넣고 꺼내오는 방식과 유사하다. (직접 로컬캐시를 구현한다면&lt;/span&gt;&lt;span&gt;ConcurrentHashMap&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;분산 시스템에서 데이터 정합성이 깨질 수 있다. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;ex) Caffeine Cache, Ehache&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Global Cache&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;여러 서버에서 캐시 서버에 접근하여 참조&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;서버간 데이터 공유 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;네트워크 트래픽 이용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;데이터 분산하여 저장 (Master / Slave, Sharding)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;ex) Redis&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;출처&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;a style=&quot;color: #4183c4;&quot; href=&quot;https://goldfishhead.tistory.com/29&quot;&gt;&lt;span&gt;Local Cache, Global Cache 차이&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;Guava Cache&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Google Guava에서 지원하는 Local Cache 라이브러리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Key-Value형태의 데이터 구조로 표현된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;캐시 크기, 리프레시, 로딩방법 등을 지정할 수 있음 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;내부적으로 ConcurrentHashMap과 유사하게 되어있어 thread safe 하다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;ConcurrentMap과 차이라고 한다면 캐시는 메모리 사용공간을 제한하기 위해서 값을 자동으로 제거하는 로직이 들어가 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;sql&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;// https://guava.dev/releases/19.0/api/docs/com/google/common/cache/LoadingCache.html
Implementations of this interface are expected to be thread-safe, and can be safely accessed by multiple concurrent threads.&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;종류 &lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;1. LoadingCache&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;필요한 값이 없을 때 데이터를 다시 로드해 줌.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;pre class=&quot;haxe&quot; style=&quot;background-color: #f8f8f8; text-align: left;&quot;&gt;&lt;code&gt;LoadingCache&amp;lt;Key, Graph&amp;gt; graphs = CacheBuilder.newBuilder()
 &amp;nbsp; &amp;nbsp; &amp;nbsp; .maximumSize(1000)
 &amp;nbsp; &amp;nbsp; &amp;nbsp; .build(
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; new CacheLoader&amp;lt;Key, Graph&amp;gt;() {
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; public Graph load(Key key) throws AnyException {
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return createExpensiveGraph(key);
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; });
​
...
try {
 &amp;nbsp;return graphs.get(key);
} catch (ExecutionException e) {
 &amp;nbsp;throw new OtherException(e.getCause());
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;build()에 CacheLoader 구현 객체를 넣어야 하고 CacheLoader구현체는 load메서드를 구현해야 한다. 인스턴스를 만든 이후에는 Map을 쓰듯이 쓰면 됨..&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;2. Cache&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;데이터를 자동으로 로드하지 않음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;캐시에서 키와 관련된 값을 가져오는 메서드&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;get() :&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt; 데이터를 로딩하는 중 Checked Exception이 발생할 경우 ExecutionException을 던진다. 그러므로 예외 처리 코드를 반드시 작성해주어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;getUnchecked() :&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt; get()과 달리 CheckedException을 던지지 않는다. 그러므로 CacheLoader가 CheckedException을 던지지 않는 상황에서만 사용해야 한다. 예외가 발생하면 RuntimeException을 던진다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;리소스 제약으로 데이터 없애는 시점 결정&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;size-based eviction : 캐시 사이즈 제한&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;maximumSize&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;maximumWeight&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;. weigher으로 가중치를 설정할 수 있음 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;time-based eviction : 시간 기반 제거&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;expireAfterAccess(long, TimeUnit)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;항목이 읽기 또는 쓰기로 마지막으로 액세스 된 이후 지정된 기간이 지난 후에만 항목이 만료됩니다. &lt;/span&gt;&lt;span&gt;&lt;a style=&quot;color: #4183c4;&quot; href=&quot;https://github.com/google/guava/wiki/CachesExplained#Size-based-Eviction&quot;&gt;&lt;span&gt;항목이 제거되는 순서는 크기 기반 제거&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;span&gt; 순서와 비슷합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;expireAfterWrite(long, TimeUnit)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;항목이 생성된 이후 지정된 기간이 경과하거나 값이 가장 최근에 대체된 후 항목을 만료합니다. 일정 시간이 지나면 캐시 된 데이터가 오래되면 바람직할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;CacheBuilder.ticker(Ticker)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;시간기반 테스트할 때는 2초 후에 만료되는지 테스트하려고 2초를 기다릴 필요 없이 Ticker를 쓰면 된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;reference-based eviction: 참조기반으로 제거 &lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;weakKeys(), weakValues() 값이나 키가 weakReference로 감싸져 weakReference만 남으면 GC 됨&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;가비지 수집은 equals대신 ==로 비교 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;softKeys(), softValues &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;제거하기 위해서 자동으로 막 찾는 건 아니고 실제로 요청이 왔을 때 근처에 수행함&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;그래서 주기적으로 Cache.cleanup 해주거나 ScheduledExecutorService를 예약하는 게 좋을 수도 있음 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;null값 처리&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;null값 캐시는 의미가 없어 null을 로드하려고 하면 예외를 발생시킴&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Optional클래스를 활용해야 함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;
&lt;pre class=&quot;processing&quot; style=&quot;background-color: #f8f8f8; text-align: left;&quot;&gt;&lt;code&gt;@Test
public void whenNullValue_thenOptional() {
 &amp;nbsp; &amp;nbsp;CacheLoader&amp;lt;String, Optional&amp;lt;String&amp;gt;&amp;gt; loader;
 &amp;nbsp; &amp;nbsp;loader = new CacheLoader&amp;lt;String, Optional&amp;lt;String&amp;gt;&amp;gt;() {
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;@Override
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;public Optional&amp;lt;String&amp;gt; load(String key) {
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;return Optional.fromNullable(getSuffix(key));
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  }
 &amp;nbsp;  };
​
 &amp;nbsp; &amp;nbsp;LoadingCache&amp;lt;String, Optional&amp;lt;String&amp;gt;&amp;gt; cache;
 &amp;nbsp; &amp;nbsp;cache = CacheBuilder.newBuilder().build(loader);
​
 &amp;nbsp; &amp;nbsp;assertEquals(&quot;txt&quot;, cache.getUnchecked(&quot;text.txt&quot;).get());
 &amp;nbsp; &amp;nbsp;assertFalse(cache.getUnchecked(&quot;hello&quot;).isPresent());
}
private String getSuffix(final String str) {
 &amp;nbsp; &amp;nbsp;int lastIndex = str.lastIndexOf('.');
 &amp;nbsp; &amp;nbsp;if (lastIndex == -1) {
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;return null;
 &amp;nbsp;  }
 &amp;nbsp; &amp;nbsp;return str.substring(lastIndex + 1);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;다른 캐시 구현 라이브러리나 기술 &lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;(일단 나는 이미 guava라이브러리를 의존성에 추가해놓고 있기 때문에 쉽게 사용할 수 있다는 점이 좋은 것 같다.)&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;apache commons&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;EHcache (@Cacheable) &lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;JVM의 힙 메모리가 아닌 offheap, disk에 저장하기 위해서 직렬화가 필요함. 캐싱할 데이터는 Serializable을 상속받은 클래스여야 함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://medium.com/finda-tech/spring-%EB%A1%9C%EC%BB%AC-%EC%BA%90%EC%8B%9C-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-ehcache-4b5cba8697e0&quot;&gt;https://medium.com/finda-tech/spring-%EB%A1%9C%EC%BB%AC-%EC%BA%90%EC%8B%9C-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-ehcache-4b5cba8697e0&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;출처&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Guava Cache (구아바&amp;nbsp;캐시)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;a href=&quot;http://okminseok.blogspot.com/2019/03/guava-cache.html&quot;&gt;http://okminseok.blogspot.com/2019/03/guava-cache.html&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://ijbgo.tistory.com/10&quot;&gt;https://ijbgo.tistory.com/10&lt;/a&gt;&lt;/span&gt;&lt;span&gt; 메서드 잘 나와있음 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://guava.dev/releases/19.0/api/docs/com/google/common/cache/LoadingCache.html&quot;&gt;https://guava.dev/releases/19.0/api/docs/com/google/common/cache/LoadingCache.html&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://github.com/google/guava/wiki/CachesExplained&quot;&gt;https://github.com/google/guava/wiki/CachesExplained&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.baeldung.com/guava-cache&quot;&gt;https://www.baeldung.com/guava-cache&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;&lt;/div&gt;
&lt;h1 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;span&gt;시간당 실행 횟수 제한(처리율 제한) &lt;/span&gt;&lt;/h1&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;시간당 실행 횟수 제한 라이브러리 3종 소개 : RateLimiter &lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://javacan.tistory.com/entry/ratelimiter-ratelimitj-bucket4j-intro&quot;&gt;https://javacan.tistory.com/entry/ratelimiter-ratelimitj-bucket4j-intro&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;RateLimiter&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;guava&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.baeldung.com/guava-rate-limiter&quot;&gt;https://www.baeldung.com/guava-rate-limiter&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;private RateLimiter limiter = RateLimiter.create(4.0); // 초당 4개 
// 여기를 만약에 0.2로 주면 5초당 1개를 허용한다는 의미 
​
public void someLimit() {
 &amp;nbsp;if (limiter.tryAcqurie()) { 
 &amp;nbsp; &amp;nbsp;
  } else {
 &amp;nbsp; &amp;nbsp;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;limiter.acquire는 허가를 받을 때까지 blocking, limiter.tryAcquire는 허가를 받지 못하면 false를 리턴 (호출 분산)&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;public boolean tryAcquire(int permits,
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; long timeout,
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; TimeUnit unit)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;permits&lt;/span&gt;&lt;span&gt; - the number of permits to acquire&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;timeout&lt;/span&gt;&lt;span&gt; - the maximum time to wait for the permits. Negative values are treated as zero.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;unit&lt;/span&gt;&lt;span&gt; - the time unit of the timeout argument&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;누출 버킷 알고리즘이라 timeout시간에 따라서 요청이 버려질 수 있다. 관련 이슈 링크는 아래 참고&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://jobc.tistory.com/230&quot;&gt;https://jobc.tistory.com/230&lt;/a&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://github.com/google/guava/issues/5262&quot;&gt;https://github.com/google/guava/issues/5262&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://sowhat4.tistory.com/76&quot;&gt;https://sowhat4.tistory.com/76&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Bucket4j&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://khj93.tistory.com/entry/Bucket4j%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-Spring-API-%EC%9A%94%EC%B2%AD-%ED%95%9C%EB%8F%84-%ED%8A%B8%EB%9E%98%ED%94%BD-%EC%A0%9C%ED%95%9C%ED%95%98%EA%B8%B0&quot;&gt;https://khj93.tistory.com/entry/Bucket4j%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-Spring-API-%EC%9A%94%EC%B2%AD-%ED%95%9C%EB%8F%84-%ED%8A%B8%EB%9E%98%ED%94%BD-%EC%A0%9C%ED%95%9C%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Bucket4j는 Token bucket 알고리즘을 기반으로 하는 Java 속도 제한 라이브러리입니다. Bucket4j는 독립 실행형 JVM 애플리케이션 또는 클러스터 환경에서 사용할 수 있는 스레드로부터 안전한 라이브러리입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.baeldung.com/spring-bucket4j&quot;&gt;https://www.baeldung.com/spring-bucket4j&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://github.com/bucket4j/bucket4j/blob/master/README.md&quot;&gt;https://github.com/bucket4j/bucket4j/blob/master/README.md&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;구현방법은 생각보다 간단해서 사용하는 알고리즘에 따라서 원하는 동작이 될지 생각하면 될 것 같다. ( 일부가 누수되어도 되는지, 순서대로 앞에 온 요청이 실행될 것이 보장은 되어야 하는지)&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;br /&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;lt;아래 더보기 )) 대규모 시스템 설계 책 4장 내용 읽어보면 좋음 &amp;gt;&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;처리율 제한 장치란&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;클라이언트 또는 서비스가 보내는 트래픽의 처리율을 제어하기 위한 장치&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;특정 기간 내에 전송되는 클라이언트의 요청 횟수 제한&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;요청 횟수가 임계치를 넘어서면 추가로 도달한 호출은 모두 중단됨&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;ex) 오픈 API에서 일정 시간당 몇 회 호출등의 제한을 걸 때&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;필요성&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Dos(Denial of Service) 공격에 의한 자원 고갈 방지&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;비즈니스 모델 활용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;비용 절감&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;처리율 제한 알고리즘&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;토큰 버킷 알고리즘&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;버킷 크기, 토큰 공급률&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;각 요청은 하나의 토큰을 소비하며 버킷에 주기적으로 공급되는 토큰의 존재 여부에 따라 요청을 전달할지, 버릴지 결정&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;장점&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;구현이 쉽고 메모리 사용 측면에서 효율적이다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;버킷에 토큰이 충분하기만 하면 시스템에 전달되기 때문에 짧은 시간에 집중되는 트래픽 처리도 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;단점&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;버킷의 크기와 토큰 공급률이라는 인자를 적절하게 튜닝하는게 까다롭다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;채용 플랫폼 - AWS(API Gateway, EC2, EBS, CPU Credit), Spring Cloud Netflix Zuul, Bucket4j&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;누출 버킷 알고리즘&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;버킷 크기, 처리율(지정된 시간에 몇 개의 항목을 처리할지), 큐&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;장점&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;큐의 크기가 제한되어있어 메모리 사용측면에서 효율적&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;고정된 처리율로 안정적 출력 가능&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;단점&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;단기간에 몰리는 트래픽의 경우 큐에 오랜 요청이 쌓이게 되고 제때 처리되지 못하면 최신 요청이 버려짐&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;입력 속도가 출력 속도보다 크면 버킷에서 누적이 발생하고 누적이 버킷 용량보다 큰 경우 오버플로가 발생하여 데이터 패킷 손실이 발생할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;버킷의 크기와 처리율이라는 인자를 적절하게 튜닝하는게 까다롭다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;채용 플랫폼: Amazon MWS, NGINX, Uber-go rate limiter, Shopify&lt;/span&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;, Guava RateLimiter&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;고정 윈도우 카운터 알고리즘&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;타임라인을 고정된 간격의 window로 나누고 각 window마다 counter를 붙임&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;요청이 접수될 때마다 counter가 1씩 증가하며, counter값이 threshold에 도달하면 새로운 요청은 새 윈도우가 열릴 때 까지 버려짐&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;장점&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;메모리 효율이 좋고 이해하기 쉬움&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;window가 닫히는 시점에 카운터를 초기화하는 방식은 특정한 트래픽 패턴을 처리하기에 적합하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;단점&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;경계시간에 몰리면 window에 할당된 양보다 더 많은 요청이 처리될 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;이동 윈도우 로깅 알고리즘&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;타임스탬프를 redis의 sorted set같은 캐시에 보관해서 고정 윈도 카운터 알고리즘을 해결&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;단점&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;타임스탬프를 보관하는 메모리 사용&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;이동 윈도우 카운터 알고리즘&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;3+4&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;채용 플랫폼: RateLimitJ&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;출처&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;대규모 시스템 설계&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;참고할 링크&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://hechao.li/2018/06/25/Rate-Limiter-Part1/&quot;&gt;https://hechao.li/2018/06/25/Rate-Limiter-Part1/&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;서비스 가용성 확보에 필요한 Rate Limiting Algorithm에 대해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://www.mimul.com/blog/about-rate-limit-algorithm/&quot;&gt;https://www.mimul.com/blog/about-rate-limit-algorithm/&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend/공부,개념</category>
      <author>지수쓰</author>
      <guid isPermaLink="true">https://w97ww.tistory.com/117</guid>
      <comments>https://w97ww.tistory.com/117#entry117comment</comments>
      <pubDate>Sat, 15 Apr 2023 21:51:24 +0900</pubDate>
    </item>
    <item>
      <title>[Kotlin] Collection, 함수, 람다, 기타 ..</title>
      <link>https://w97ww.tistory.com/116</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;출처 - 인프런 자바개발자를 위한 코틀린 입문 강의&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;a href=&quot;https://www.inflearn.com/course/java-to-kotlin&quot;&gt;https://www.inflearn.com/course/java-to-kotlin&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;CH15. 배열과 컬렉션&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;배열&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;val array = arrayOf(100, 200)
​
for (i in array.indices) {
 &amp;nbsp;println(&quot;${i} ${array[i]}&quot;)
}
​
for ((idx, value) in array.withIndex()) {
 &amp;nbsp;println(&quot;${idx} ${value}&quot;)
}
​
array.plus(300)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;인덱스 가져오기 : &lt;/span&gt;&lt;span&gt;array.indices&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;인덱스랑 값 같이 가져오기 : &lt;/span&gt;&lt;span&gt;array. withIndex()&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;배열에 값 추가하기 : &lt;/span&gt;&lt;span&gt;array.plus&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;Collection&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;코틀린은 불변, 가변을 지정해주어야한다. (기본은 불변)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;가변컬렉션 : 컬렉션에 element 추가, 삭제 가능&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;listOf&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setOf&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;mapOf&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;불변컬렉션 : 컬렉션에 element 추가, 삭제 불가&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;mutableListOf&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;mutableSetOf&lt;/span&gt;&lt;span&gt; ,&lt;/span&gt;&lt;span&gt;mutableMapOf&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;불변이라고 하더라도 reference타입 안에 있는 필드는 바꿀 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// 선언 
// 기본적으로는 불변
val numbers = listOf(100, 200) 
val emptyList = emptyList&amp;lt;Int&amp;gt;() // 비어있고 타입 추론이 불가능하다면 &amp;lt;&amp;gt;에 타입을 적어주어야함
​
// 가변리스트
val numbers = mutableListOf(100, 200)
numbers.add(300)
​
// 가져오기
numbers.get(0)
numbers[0]
​
// for문
for (number in numbers) {
 &amp;nbsp;println(number)
}
​
for ((idx, value) in numbers.withIndex()) {
 &amp;nbsp;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;map&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;val oldMap = mutableMapOf&amp;lt;Int, String&amp;gt;() // 타입 지정
oldMap[1] = &quot;MONDAY&quot;
oldMap[2] = &quot;TUSEDAY&quot;
​
mapOf(1 to &quot;MONDAY&quot;, 2 to &quot;TUESTDAY&quot;) // to라는 중위호출 사용
​
for (key in oldMap.keys) {
 &amp;nbsp;println(key)
 &amp;nbsp;println(oldMap[key])
}
​
for ((key,value)) in oldMap.entries) {
 &amp;nbsp;println(key)
 &amp;nbsp;println(value)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;컬렉션의 null가능성&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;List&amp;lt;Int?&amp;gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;리스트에 null이 들어갈 수 있지만 리스트는 not null&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;List&amp;lt;Int&amp;gt;?&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;리스트에 null이 들어갈 수 없지만 리스트는 nullable&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;List&amp;lt;Int?&amp;gt;?&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;둘다 nullable&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;Java와 같이 사용하는 Collection&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;자바에서 코틀린의 불변 컬렉션을 가져와서 구분없이 리스트에 값을 추가하고 다시 코틀린에 돌려줄 수 있음. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;아니면 코틀린 쪽에서 자바의 &lt;/span&gt;&lt;span&gt;Collection.unmodifiableXX&lt;/span&gt;&lt;span&gt;이런거로 사용해서 방어를 할 수도 있을것&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;CH16. 함수&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;확장함수&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;//EX. String클래스 안에 있는 것 같은 확장함수 
​
fun main() {
 &amp;nbsp;val str = &quot;ABC&quot;
 &amp;nbsp;println(str.lastChar())
}
​
fun String.lastChar(): Char {
 &amp;nbsp;return this[this.length-1]
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;String클래스를 확장하므로 &lt;/span&gt;&lt;span&gt;String.&lt;/span&gt;&lt;span&gt; 으로 쓰고 (수신객체 타입) &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt; (수신 객체) 를 통해 실제 클래스 안의 값에 접근함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;확장함수는 &lt;/span&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;원본 클래스의 private, protected에 접근하지 못함&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;원래 있는 클래스와 &lt;/span&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;같은 시그니처 메서드를 호출하면 멤버함수(원본)가 호출된다.&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;확장함수를 만들고 나중에 멤버함수에 같은 메서드가 생긴다면 오류가 생길 수 있음 (주의)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;오버라이드 된 확장함수&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;val tran: Tran = Train()
train.isExpensive() // Train의 확장함수
​
val srt1: Train = Srt()
srt1.isExpensive() // Train의 확장함수
​
val srt2: Srt = Srt()
srt2.isExpensive() // Srt의 확장함수&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;해당 변수의 현재 타입(&lt;/span&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;즉, 정적인 타입&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt;)에 의해 어떤 확장함수를 호출할지 결정됨&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;확장프로퍼티&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;확장 함수와 비슷하게 프로퍼티도 만들 수 있음(ex. custom getter)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;infix함수(중위함수) : 변수 {함수이름} argument&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;함수를 호출하는 새로운 방법&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;fun Int.add(other: Int): Int {
 &amp;nbsp;return this + other
}
​
infix fun Int.add2(other: Int):Int {
 &amp;nbsp;return this + other
}
​
// 3.add(4)
// 3.add2(4)
3 add2 4&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;inline함수 &lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;함수가 호출되는 대신 함수가 호출하는 지점에 본문을 그대로 복사하는것 &lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;바이트코드로 봤을 때 알 수 있음 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;함수를 파라미터로 전달할 때 오버헤드를 줄일 수 있다.&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;성능측정과 함께 신중하게 사용되어야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;fun main() {
 &amp;nbsp;3.add(4)
}
​
inline fun Int.add(other: Int): Int {
 &amp;nbsp;return this + other
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;지역함수&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;함수 안에 함수를 선언할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;함수로 추출하면 좋을 것 같은데 이 함수를 지금 함수에서만 사용하고 싶을 때 &lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;단 depth가 깊어지기도 하고 그렇게 깔끔하진 않을 수도 있음...&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;fun createPerson(~~): Person {
 &amp;nbsp;fun validateName(name: String, fieldName: String) {
 &amp;nbsp; &amp;nbsp;if (name.isEmpty) {
 &amp;nbsp; &amp;nbsp; &amp;nbsp;throw IllegalArgumentException()
 &amp;nbsp;  }
  }
 &amp;nbsp;validateName(firstName, &quot;firstName&quot;)
 &amp;nbsp;validateName(lastName, &quot;lastName&quot;)
 &amp;nbsp;
 &amp;nbsp;return Person(firstName, lastName, 1)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;CH17. 람다&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;코틀린에서의 람다&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;코틀린에서는 함수가 그 자체로 값이 될 수 있다. (1급시민)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;변수에 할당할 수도 있고, 파라미터로 넘길 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;val fruits = listOf(..)

// 람다 생성 방법 
val isApple: (Fruit) -&amp;gt; Boolean = fun(fruit: Fruit): Boolean {
  return fruit.name == &quot;사과&quot;
}

val isApple2: (Fruit) -&amp;gt; Boolean = { fruit: Fruit -&amp;gt; fruit.name == &quot;사과&quot;}

// 람다 호출 방법
isApple(fruits[0])
isApple.invoke(fruits[0])&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;private fun filterFruits(
  fruits: List&amp;lt;Fruit&amp;gt;, filter: (Fruit) -&amp;gt; Boolean
): List&amp;lt;Fruit&amp;gt; {
  val results = mutableListOf&amp;lt;Fruit&amp;gt;()
  for (fruit in fruits) {
    if (filter(fruit)){
      results.add(fruit)
    }
  }
  
  return results
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;// 다 가능 
filterFruits(fruits, isApple)
filterFruits(fruits, isApple2)

// 코틀린 스럽게 바꿔보기 
filterFruits(fruits, { fruit: Fruit -&amp;gt; fruit.name == &quot;사과&quot;})
filterFruits(fruits) { fruit: Fruit -&amp;gt; fruit.name == &quot;사과&quot;} // 1
filterFruits(fruits) { fruit -&amp;gt; fruit.name == &quot;사과&quot;} // 2 
filterFruits(fruits) { it.name == &quot;사과&quot; } //3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;1. 소괄호 밖으로 중괄호 람다 빼기&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;함수(파라미터1, 파라미터2, ..., {중괄호파라미터})&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;함수(파라미터1, 파라미터2, ...) {중괄호파라미터}&lt;/span&gt;&lt;span&gt; 이렇게 쓸 수 있다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;마지막 파라미터가 중괄호로 되어있으면 소괄호 밖으로 중괄호를 빼면 함수 호출할 때 중괄호가 가장 마지막 파라미터로 들어감 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;람다는 여러줄 작성할 수 있고 마지막 줄의 결과가 람다의 반환값임&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;2. 타입추론 가능한거 타입 빼기&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;3. 파라미터가 한개인 경우 익명함수에서 &lt;/span&gt;&lt;span&gt;it&lt;/span&gt;&lt;span&gt; 이라고 쓸 수 있음 &lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;그래도 it 말고 이름 써주는걸 추천한다고 함 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;Closure&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;자바&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;String targetFruitName = &quot;바나나&quot;;
targetFruitName = &quot;수박&quot;;
filterFruits(fruits, (fruit) -&amp;gt; targetFruitName.equals(fruit.getname()));
// 자바에서는 final 아닌 변수를 람다에서 쓰는것에서 오류 발생시킴&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;코틀린&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;var targetFruitName = &quot;바나나&quot;
targetFruitName = &quot;수박&quot;
filterFruits(fruits) { it.name == targetFruitName} // 됨&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;코틀린에서는 람다가 시작하는 지점에 참조하는 모든 변수들을 모두 포획하여 가지고 있음. 이렇게 해야만 람다를 진정한 일급시민으로 간주할 수 있기 때문&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;이 데이터 구조를 Closure라고 부른다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;try with resources&lt;/span&gt;&lt;/h4&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;public inline fun &amp;lt;T : Closeable?, R&amp;gt; T.use(block: (T) -&amp;gt; R):R {
  
} 
// Cloesable 구현체 T에 대한 확장함수 
// inline함수
// 람다를 받게 만들어진 함수&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;CH18. 컬렉션을 함수형으로 다루는 방법&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;필터와 맵&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;filter&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;val apples = fruits.filter { fruit -&amp;gt; fruit.name == &quot;사과&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;filterIndexed&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;val apples = fruits.filterIndexed { idx, fruit -&amp;gt; 
 println(idx)
 fruit.name == &quot;사과&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;map &lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;val applePrices = fruits.filter { fruit -&amp;gt; fruit.name == &quot;사과&quot;}
.map { fruit -&amp;gt; fruit.currentPrice }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;mapIndexed&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;val applePrices = fruits.filter { fruit -&amp;gt; fruit.name == &quot;사과&quot;}
.mapIndexed { idx, fruit -&amp;gt;
  println(idx)
  fruit.currentPrice 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;mapNotNull&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;val values = fruits.filter { fruit -&amp;gt; fruit.name == &quot;사과&quot;}
  .mapNotNull { fruit -&amp;gt; fruit.nullOrValue() }&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;다양한 컬렉션 처리 기능&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;all : 조건을 모두 만족하면 true 그렇지 않으면 false&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;val isAllApple = fruits.all { fruit -&amp;gt; fruit.name == &quot;사과&quot; }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;none : 조건을 모두 불만족하면 true 그렇지않으면 false&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;any : 조건을 하나라도 만족하면 true 그렇지 않으면 false &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;count : 개수를 센다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;sortedBy : (오름차순) 정렬을 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;sortedByDescending : 내림차순&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;distincyBy : 변형된 값을 기준으로 중복을 제거한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;first : 첫번째 값을 가져온다. (null이 무조건 아니어야함)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;firstOrNull : 첫번째 값 또는 null을 가져온다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;last , lastOrNull&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;List를 Map으로&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;groupBy &lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;과일이름 (Key) -&amp;gt; List&amp;lt;과일&amp;gt; (value) Map &lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;val map: Map&amp;lt;String, List&amp;lt;Fruit&amp;gt;&amp;gt; = fruits.groupBy {fruit -&amp;gt; fruit.name}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;과일이름 (key) -&amp;gt; List&amp;lt;출고가&amp;gt; (value) Map&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;val map: Map&amp;lt;String, List&amp;lt;Long&amp;gt;&amp;gt; = fruits
.groupBy( {fruit -&amp;gt; fruit.name}, fruit -&amp;gt; fruit.factoryPrice})&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;associateBy : id를 통해서 중복되지 않는 키를 가지고 뭔가 매핑해야할 때 &lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;id (key) -&amp;gt; 과일(Value)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;val map: Map&amp;lt;Long, Fruit&amp;gt; = fruits.associateBy { fruit -&amp;gt; fruit.id }	&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;id -&amp;gt; 출고가&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;val map: Map&amp;lt;Long, Long&amp;gt; = fruits
.associateBy({ fruit -&amp;gt; fruit.id}, {fruit -&amp;gt; fruit.factoryPrice})&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;중첩된 컬렉션 처리&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;val fruitsInList: List&amp;lt;List&amp;lt;Fruit&amp;gt;&amp;gt; = listOf(
 listOf(),
 listOf(),
  ...
)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;flatMap&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;출고가와 현재가가 동일한 과일을 고를 때&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;pre class=&quot;ocaml&quot;&gt;&lt;code&gt;val samePriceFruits = fruitsInList.flatMap {
 &amp;nbsp;list -&amp;gt;
 &amp;nbsp; &amp;nbsp;list.filter {fruit-&amp;gt;fruit.factoryPrice == fruit.currentPrice}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;리팩토링&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;val samePriceFruits = fruitsInList.flatMap {list -&amp;gt; list.samePriceFilter}
​
// List에 대한 확장함수 만들어서 호출 
val List&amp;lt;Fruit&amp;gt;.samePriceFilter: List&amp;lt;Fruit&amp;gt;
 &amp;nbsp;get() = this.filter(Fruit::isSamePrice)&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;flatten&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;중첩된 컬렉션 해제 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;CH19. 코틀린 이모저모 &lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Type Alias 긴 이름의 클래스 혹은 함수타입 네이밍&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;typealias FruitFilter = (Fruit) -&amp;gt; Boolean

fun filterFruits(fruits: List&amp;lt;Fruit&amp;gt;, filter: FruitFilter) {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;as import : 클래스, 함수 import할 때 이름을 바꾸는 기능 &lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;import ~~~.printHelloWorld as printHelloWorldA
import ~~~.printHelloWorld as printHelloWolrdB

fun main() {
  printHelloWorldA()
  printHelloWorldB()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;구조분해 : 복합적인 값을 분해하여 여러 변수를 한 번에 초기화하는것&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;data class Person(
  val name: String,
  val age: Int
)
val person = Person(&quot;강지수&quot;, 27)
val (name, age) = person

println(&quot;이름 : ${name}, 나이 : ${age}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Data Class는 &lt;/span&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;component{N}&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt;이란 함수를 자동으로 만들어준다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;pre class=&quot;fsharp&quot;&gt;&lt;code&gt;// 구조분해를 풀어보면 이렇게 됨. name, age를 인식하는 것은 아님 
val name = person.compoent1()
val age = person.component2()&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Data Class가 아닌 경우 componentN함수를 구현하면 된다. &lt;/span&gt;&lt;span&gt;operator&lt;/span&gt;&lt;span&gt; 로 연산자 오버로딩을 해줘야한다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;class Person(
  val name: String
) {
  operator fun component1(): String {
    return this.name
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;for ( (key, value) in map.entries)&lt;/span&gt;&lt;span&gt; 도 구조분해 문법의 일종이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;jump와 Label&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;jump&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;return : 기본적으로 가장 가까운 enclosing function 또는 익명함수로 값이 반환&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;break : 가장 가까운 루프가 제거&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;continue : 가장 가까운 루프를 다음 step으로 보냄 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt;에서는 break, continue를 쓸 수 없음! (run + return or 일반 for, while문)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;label&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;라벨 명시하면 jump@label 가능...&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;사용하지 않는 것을 강력추천!!! &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;TakeIf와 TakeUnless&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;takeIf : 주어진 조건을 만족하면 그 값이, 그렇지 않으면 null이 반환된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;takeUnless : 주어진 조건을 만족하지 않으면 그 값이, 그렇지 않으면 null이 반환&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;CH20. scope function&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;scope function : 일시적인 영역을 형성하는 함수&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;// 기본 코드
fun printPerson(person: Person?) {
  if (person != null) {
    println(person.name)
    println(person.age)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;ex) let (람다를 받아 람다 결과를 반환하는 확장함수) 사용&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;// 기본 코드
fun printPerson(person: Person?) {
  person?.let {
    println(it.name)
    println(it.age)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;inline fun &amp;lt;T, R&amp;gt; T.let(block: (T) -&amp;gt; R): R {
  return block(this)
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;종류 , usage&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;let &lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;하나이상의 함수를 call chain 결과로 호출할 때 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;non-null 값에 대해서만 code block을 실행시킬 때 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;일회성으로 제한된 영역에 지역변수를 만들 때 (는 메서드분리가 나을지도)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;run&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;객체 초기화와 반환 값의 계산을 동시에 해야할 때&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;also&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;객체를 수정하는 로직이 call chain 중간에 필요할 때 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;apply&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;객체 설정을 할 때 객체 수정하는 로직이 call chain 중간에 필요할 때(ex. Test Fixture 만들 때)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;with (확장함수 X 람다 )&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;this 사용해 접근하고 this는 생략 가능 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;특정 객체를 다른 객체로 변환해야 하는데, 모듈 간의 의존성에 의해 정적 팩토리 혹은 toClass함수를 만들기 어려울 때&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;it 사용&lt;/span&gt;&lt;span style=&quot;color: #a7a7a7;&quot;&gt; &lt;/span&gt;&lt;span&gt;it: 생략 불가, 다른 이름 붙일 수 있음&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;this 사용&lt;/span&gt;&lt;span style=&quot;color: #a7a7a7;&quot;&gt; &lt;/span&gt;&lt;span&gt;this : 생략 가능, 다른 이름 붙일 수 없음&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;람다의 결과&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;let&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;run&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;객체 그 자체&lt;/span&gt;&lt;span style=&quot;color: #a7a7a7;&quot;&gt; &lt;/span&gt;&lt;span&gt;(람다 결와 무관)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;also&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;apply&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;val value1 = person.let { //value1 = age
  it.age 
}

val value2 = person.run { //value2 = age
  this.age
}

val value3 = person.also { //value3 = person
  it.age
}

val value4 = person.apply { //value4 = person
  this.age
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;// let은 일반함수를 받음. 파라미터를 받고 파라미터에 대한 이름을 직접 넣어줄 수 있음
inline fun &amp;lt;T, R&amp;gt; T.let(block: (T) -&amp;gt; R): R {
  return block(this)
}

// run은 확장함수를 받음. 본인 자신을 this로 호출하고 생략할 수 있음
inline fun &amp;lt;T, R&amp;gt; T.run(block: T.() -&amp;gt; R):R {
  return block()
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Backend/코틀린</category>
      <author>지수쓰</author>
      <guid isPermaLink="true">https://w97ww.tistory.com/116</guid>
      <comments>https://w97ww.tistory.com/116#entry116comment</comments>
      <pubDate>Sun, 26 Mar 2023 22:15:00 +0900</pubDate>
    </item>
    <item>
      <title>[Kotlin] 클래스, 상속, 접근제어, object</title>
      <link>https://w97ww.tistory.com/115</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;출처 - 인프런 자바개발자를 위한 코틀린 입문 강의 ( 9~14강 )&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;a href=&quot;https://www.inflearn.com/course/java-to-kotlin&quot;&gt;https://www.inflearn.com/course/java-to-kotlin&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;CH9. 클래스를 다루는 방법&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;코틀린에서는 필드를 만들면 getter와 setter를 자동으로 만든다.&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;필드 하나를 프로퍼티라고 부름&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;코틀린에서는 주 생성자가 필수임&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;custom getter, setter를 만들 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;생성자&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;delphi&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;class Person {
 &amp;nbsp;val name: String,
 &amp;nbsp;var age: Int,
} {
 &amp;nbsp;init { // 가장먼저호출
 &amp;nbsp; &amp;nbsp;if (age &amp;lt; 0) {
 &amp;nbsp; &amp;nbsp; &amp;nbsp;throw new Exception()
 &amp;nbsp;  }
  }
 &amp;nbsp;constructor(name: String): this(name, 1){
 &amp;nbsp; &amp;nbsp;println(&quot;첫번째 부 생성자&quot;)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;부생성자보다는 default parameter 또는 정적팩토리메서드를 권장함&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;class Person {
 &amp;nbsp;val name: String = &quot;홍길동&quot;,
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;custom getter&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;class Person {
 &amp;nbsp;val name: String,
 &amp;nbsp;var age: Int,
} {
 &amp;nbsp;
 &amp;nbsp;// 원래함수형태
 &amp;nbsp;fun isAdult(): Boolean {
 &amp;nbsp; &amp;nbsp;return this.age &amp;gt;= 20
  }
 &amp;nbsp;
 &amp;nbsp;// Custom getter 
 &amp;nbsp;val isAdult: Boolean {
 &amp;nbsp; &amp;nbsp;get() = this.age &amp;gt;= 20
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;객체의 속성을 나타내는것 : custom getter&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;아니면 함수형태&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예) 이름 대문자로 바꾸는 예제&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;class Person (
 &amp;nbsp;name: String = &quot;jisu&quot;,
) {
 &amp;nbsp;val name = name
 &amp;nbsp; &amp;nbsp;get() = field.uppercase()
 &amp;nbsp;//get() = name.uppsercase() 이렇게쓰면 무한루프 발생돼서 자기자신 가리키는 필드를 의미하는 field 예약어 씀 (backing field)
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;class Person (
  name: String = &quot;jisu&quot;,
) {
 &amp;nbsp;val uppercaseName: String 
 &amp;nbsp; &amp;nbsp;get() = this.name.uppercase()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;backing field말고 this.name 으로 써도 됨&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;custom setter&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;setter 자체는 지향하기때문에 custom setter는 많이 안 씀&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;class Person (
  name: String = &quot;jisu&quot;,
) {
 &amp;nbsp;var name = name
 &amp;nbsp; &amp;nbsp;set(value) {
 &amp;nbsp; &amp;nbsp; &amp;nbsp;field = value.uppercase()
 &amp;nbsp;  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;CH10. 상속을 다루는 방법&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;상속 또는 구현을 할 때 &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 사용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;상위 클래스 상속을 구현할 때 생성자를 반드시 호출해야 한다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;override를 필수로 붙여야 한다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;추상멤버가 아니면 기본적으로 오버라이드가 불가능 -&amp;gt; open 사용&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;추상클래스&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;abstract class Animal(
  protected val species: String,
 &amp;nbsp;protected open val legCount: Int, // 프로퍼티 오버라이드할 때 open 붙여주어야함
) {
 &amp;nbsp;abstract fun move()
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;class Cat (
 &amp;nbsp;species: String
) : Animal(species, 4){ // :로 상속받으면서 바로 super를 불러줌
 &amp;nbsp;
 &amp;nbsp;override fun move() { // 어노테이션 대신 override
 &amp;nbsp; &amp;nbsp;println(&quot;고양이&quot;)
  }
}
​
class Penguin(
 &amp;nbsp;val species: String
) : Animal(species, 2) {
 &amp;nbsp;private val wingCount: Int = 2
 &amp;nbsp;
 &amp;nbsp;override fun move() {
 &amp;nbsp; &amp;nbsp;println(&quot;펭귄&quot;)
  }
 &amp;nbsp;
 &amp;nbsp;override val legCount: Int
 &amp;nbsp; &amp;nbsp;get() = super.legCount + this.wingCount
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;java의 extends 대신 &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 를 사용해서 상속받음&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;변수 타입은 &lt;/span&gt;&lt;span&gt;species: String&lt;/span&gt;&lt;span&gt;한 칸 안띄고 : 씀&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;상속받을 때는 &lt;/span&gt;&lt;span&gt;Cat () : Animal&lt;/span&gt;&lt;span&gt; 한칸 띄고 씀&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;open 키워드&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;프로퍼티 오버라이드 할 때 open을 붙여줘야 함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;인터페이스&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;interface Flyable {
 &amp;nbsp;fun act() { // default 쓰지 않아도 java default함수처럼 됨
 &amp;nbsp; &amp;nbsp;println(&quot;파닥파닥&quot;)
  }
 &amp;nbsp;
 &amp;nbsp;fun fly() // 추상메서드
}
​
interface Swimable {
 &amp;nbsp;println(&quot;어푸 어푸&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;class Penguin(
 &amp;nbsp;val species: String
) : Animal(species, 2), Swimable, Flyable {// 그냥 :로 인터페이스도 구현 
 &amp;nbsp;private val wingCount: Int = 2
 &amp;nbsp;
 &amp;nbsp;override fun move() {
 &amp;nbsp; &amp;nbsp;println(&quot;펭귄&quot;)
  }
 &amp;nbsp;
 &amp;nbsp;override val legCount: Int
 &amp;nbsp; &amp;nbsp;get() = super.legCount + this.wingCount
 &amp;nbsp;
 &amp;nbsp;override fun act() {
 &amp;nbsp; &amp;nbsp;super&amp;lt;Swimable&amp;gt;.act() // 특정 상위 인터페이스 함수 오버라이드할 때 꺽쇠로 표현
 &amp;nbsp; &amp;nbsp;super&amp;lt;Flyable&amp;gt;.act()
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;구현은 &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;중복 인터페이스 특정할 때는 super&amp;lt;타입&amp;gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;클래스 상속할 때 주의할 점&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;코틀린은 클래스를 상속받으려면 open 키워드를 써줘야 함. 기본은 불가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;상위 클래스를 설계할 때 생성자 또는 초기화블록에 사용되는 프로퍼티에는 open을 피해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;상속 관련 키워드&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;final : override 할 수 없게 함. default값&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;open : override를 열어준다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;abstract: override 강제&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;override: 상위 타입을 오버라고 하고 있다는 의미로 사용하는 키워드&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;CH11. 접근제어를 다루는 방법&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;가시성 제어&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;java&lt;/span&gt;&lt;span&gt;kotlin&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;text-align: left; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;public&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;모든 곳에서 접근 가능&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;모든 곳에서 접근 가능&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #f8f8f8;&quot;&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;같은 패키지 또는 하위클래스&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;선언된 클래스 또는 하위클래스&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;default&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;같은 패키지에서만 사용 가능&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #f8f8f8;&quot;&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;internal&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;같은 모듈에서만 접근 가능&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;private&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;선언된 클래스 내에서만 가능&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;선언된 클래스에서만 접근 가능&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;코틀린에서는 패키지를 가시성제어에는 사용하지 않음 -&amp;gt; 모듈&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;kotlin의 기본 접근지시 : public ( java는 default )&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;파일의 접근제어(. kt)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;파일에 변수, 함수, 클래스 여러 개 만들 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;protected는 클래스단위로 쓰이는 거라 파일에서는 사용할 수 없음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;기타&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;생성자&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt;에 접근지시어를 붙이려면 생략된 constructor를 명시해서 써야 함&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;class Cat internal constructor&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;파일&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt;최상단에 .kt 유틸용 파일을 만들면 편하다&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;자바에서 static 메서드 + private생성자 만들어 쓴 거랑 비슷&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;프로퍼티&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt; 가시성은 생성자에 지시어를 붙이거나, custom getter, setter 만들면서 get, set에다가 만 붙여줄 수 있음 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;자바와 코틀린이 protected, internal이 의미가 달라서 같이 쓸 때 주의해야 함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;CH12. object 키워드&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;static 함수와 변수&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;class Person private constructor (
 &amp;nbsp;var name: String,
 &amp;nbsp;var age: Int,
) {
 &amp;nbsp;companion object Factory : Log { // Log 인터페이스 구현
 &amp;nbsp; &amp;nbsp;private const val MIN_AGE = 0 // const: 진짜 상수
 &amp;nbsp; &amp;nbsp;
 &amp;nbsp; &amp;nbsp;//@JvmStatic
 &amp;nbsp; &amp;nbsp;fun newBaby(name: String): Person {
 &amp;nbsp; &amp;nbsp; &amp;nbsp;return Person(name, MIN_AGE)
 &amp;nbsp;  }
 &amp;nbsp; &amp;nbsp;
 &amp;nbsp; &amp;nbsp;override fun log() {
 &amp;nbsp; &amp;nbsp; &amp;nbsp;println(&quot;Person 클래스 동행객체 Factory&quot;)
 &amp;nbsp;  }
  }
}
​
interface Log {
 &amp;nbsp;fun log()
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;코틀린은 &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; 키워드 대신 &lt;/span&gt;&lt;span&gt;companion object&lt;/span&gt;&lt;span&gt; 사용&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;static : 클래스가 인스턴스화될 때 새로운 값이 복제되는 게 아니라 정적으로 인스턴스끼리의 값 공유&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;companion object: 클래스와 동행하는 유일한 오브젝트&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;val과
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;val : 런타임시 할당&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;const val : 컴파일 시 할당, 진짜 상수. 기본값과 String에만 붙일 수 있다 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;companion object는 하나의 객체로 간주되어 인터페이스 구현, 이름 짓기 모두 가능 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;자바에서 코틀린의 companion을 사용하고 싶을 때&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;이름이 없을 때 &lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Person.Companion.newBaby(&quot;ABC&quot;)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;@JvmStatic&lt;/span&gt;&lt;span&gt; 어노테이션 붙여줬을 때&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Person.newBaby(&quot;ABC&quot;)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;이름 있을 때&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Person.Factory.newBaby(&quot;ABC&quot;)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;싱글톤 : 단 하나의 인스턴스만 갖는 클래스&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;자바의 싱글톤&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;public class JavaSingleton {
 &amp;nbsp;private static final JavaSingleton INSTANCE = new JavaSingleton();
 &amp;nbsp;private JavaSingleton() {  }
 &amp;nbsp;public static JavaSingleton getInstance() {
 &amp;nbsp; &amp;nbsp;return INSTANCE;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;코틀린의 싱글톤 -&amp;gt; object만 붙여주면 됨&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;//생성
object Singleton {
 &amp;nbsp;var a: Int = 0
}
​
//사용
fun main() {
 &amp;nbsp;println(Singleton.a)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;익명 클래스&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;특정 인터페이스 클래스를 상속받은 구현체를 일회성으로 사용할 때 파라미터에 &lt;/span&gt;&lt;span&gt;object : 타입이름&lt;/span&gt;&lt;span&gt; 써서 구현&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;fun main() {
 &amp;nbsp;moveSomething(object : Movable {
 &amp;nbsp; &amp;nbsp;override fun move() {
 &amp;nbsp; &amp;nbsp; &amp;nbsp;println(&quot;무브&quot;)
 &amp;nbsp;  }
 &amp;nbsp; &amp;nbsp;override fun fly() {
 &amp;nbsp; &amp;nbsp; &amp;nbsp;println(&quot;플라이&quot;)
 &amp;nbsp;  }
  })
}
​
private fun moveSomthing(movable: Movable) {
 &amp;nbsp;movable.move()
 &amp;nbsp;movable.fly()
}&lt;/code&gt;&lt;/pre&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;CH13. 중첩 클래스&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;중첩 클래스 종류 (java 기준)&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;static을 사용하는 중첩클래스&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;: 코틀린 기본(권장)&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;클래스 안에 static을 붙인 클래스는 바깥 클래스 직접 참조 불가&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; text-align: left;&quot;&gt;&lt;code&gt;class House(
  private val address: String,
 &amp;nbsp;private val livingRoom: LivingRoom
) {
 &amp;nbsp;class LivingRoom(
    private val area: Double
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;static을 사용하지 않는 중첩클래스&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;inner class&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt; : 코틀린에서 &lt;/span&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;inner 키워드&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt; 붙여줘야함&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;바깥 클래스 참조 가능 &lt;/span&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;this.@바깥클래스&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; text-align: left;&quot;&gt;&lt;code&gt;class House(
  var address: String,
) {
 &amp;nbsp;var livingRoom = this.LivingRoom(10.0)
 &amp;nbsp;
 &amp;nbsp;inner class LivingRoom(
    private val area: Double
  ) {
 &amp;nbsp; &amp;nbsp;val address: String
 &amp;nbsp; &amp;nbsp; &amp;nbsp;get() = this@House.name // 바깥클래스 참조
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;local class&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;anonymous class&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; text-align: left;&quot;&gt;&lt;code&gt;class House(
  private val address: String,
 &amp;nbsp;private val livingRoom: LivingRoom
) {
 &amp;nbsp;class LivingRoom(
    private val area: Double
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; text-align: left;&quot;&gt;&lt;code&gt;class House(
  private val address: String,
 &amp;nbsp;private val livingRoom: LivingRoom
) {
 &amp;nbsp;class LivingRoom(
    private val area: Double
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;CH14. 다양한 클래스 다루는 방법&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;Data Class&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;getter, setter&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;equals and hashCode&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;toString&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;data class PersonDTO(
  val name: String,
 &amp;nbsp;val age: Int,
)
// 자동으로 다 만들어줌&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;*&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;java에서도 jdk16부터 비슷한 record class 도입했음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;Enum Class&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;특징 : 상속 X , 인터페이스 구현O, 싱글톤&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;kotlin에서 &lt;/span&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;when&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt; 으로 분기처리를 쉽게 할 수 있음&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;else 사용 X&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;enum 변화 체크 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;crystal&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;enum class Country (
 &amp;nbsp;private val code: String,
) {
 &amp;nbsp;KOREA(&quot;KO&quot;),
 &amp;nbsp;AMERICA(&quot;US&quot;)
  ;
}
​
​
​
fun handleCountry(country: Country) {
 &amp;nbsp;when (country) {
 &amp;nbsp; &amp;nbsp;Country.KOREA -&amp;gt; TODO()
 &amp;nbsp; &amp;nbsp;Country.AMERICA -&amp;gt; TODO()
 &amp;nbsp; &amp;nbsp;// else 를 작성 할 필요가 없음
 &amp;nbsp; &amp;nbsp;// enum에 다른 값이 추가되면 경고를 줌
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;Sealed Class, Sealed Interface&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;등장배경 ) 상속이 가능하도록 추상클래스를 만들지만 외부에서는 이 클래스를 상속받지 않았으면 좋겠음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;컴파일 타임 때 하위 클래스의 타입을 모두 기억해서 런타임 때 클래스 타입이 추가될 수 없음&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;하위 클래스는 같은 패키지에 있어야 함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;Enum과 다른 점&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;클래스 상속 O&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;하위 클래스 멀티 인스턴스 가능 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;추상화가 필요한 Entity나 DTO에서 사용하고 JDK17에서도 sealed Class가 추가되었음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;&lt;code&gt;/// 선언 
sealed class HyundaiCar(
 &amp;nbsp;val name: String,
 &amp;nbsp;val price: Long
)
​
// 같은 패키지 또는 같은 파일에 구현되어야함 
class Avante : HyundaiCar(~~)
class Sonata : HyundaiCar(~~)
class Grandeur : HyundaiCar(~~)
​
​
/// 사용
fun main() {
 &amp;nbsp;handleCar(Avante())
}
​
private fun handleCar(car: HyundaiCar) {
 &amp;nbsp;when (car) {
 &amp;nbsp; &amp;nbsp;is Avante -&amp;gt; TODO()
 &amp;nbsp; &amp;nbsp;is Grandeur -&amp;gt; TODO()
 &amp;nbsp; &amp;nbsp;is Sonata -&amp;gt; TODO()
  }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Backend/코틀린</category>
      <author>지수쓰</author>
      <guid isPermaLink="true">https://w97ww.tistory.com/115</guid>
      <comments>https://w97ww.tistory.com/115#entry115comment</comments>
      <pubDate>Mon, 20 Mar 2023 17:10:41 +0900</pubDate>
    </item>
    <item>
      <title>헥사고날 아키텍처 공부중..</title>
      <link>https://w97ww.tistory.com/114</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;쓰면서 정리하고 있어서 좀 더 보완해서 다시 수정할 예정&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;레이어드 아키텍처&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;인터페이스를 통해 의존성을 낮춘다고 해도, 데이터 액세스 계층이 변경되었을 때 비즈니스 로직 계층의 클래스가 데이터 액세스 계층에 존재하는 인터페이스를 의존하기 때문에 영향을 받을 수밖에 없다. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;데이터 액세스 인터페이스의 위치&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;데이터 액세스 계층에 존재 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;항상 하위 계층에 의존해야함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;애플리케이션 -&amp;gt; 비즈니스 로직이 핵심임 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;프레젠테이션, 데이터 액세스 계층을 저수준영역이라 하고 비즈니스 영역을 고수준영역이라고 하는데 레이어드 아키텍처의 규칙에 따르면 고수준 영역이 저수준 영역에 의존하게 되는 상태&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;DIP를 이용해 데이터 액세스 계층에서 정의한 인터페이스 위치를 비즈니스로직 계층으로 이동시켰음. 데이터 액세스 계층의 구현체는 비즈니스 로직 계층의 인터페이스를 바라보게 됨&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;배경&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;애플리케이션이 중심이고 DB, 웹 프레임워크 등은 애플리케이션이 사용하는 부품으로 보는 아키텍처가 주목받기 시작한다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;대표적으로 헥사고날 아키텍처와 클린 아키텍처&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;헥사고날아키텍처 &lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;포트 앤 어댑터 아키텍처(ports and adapters architecture) by Alistair Cockburn&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1428&quot; data-origin-height=&quot;1112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Vh43H/btr06XCU5o3/9BkavadFUjdqXQ4TZCpAw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Vh43H/btr06XCU5o3/9BkavadFUjdqXQ4TZCpAw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Vh43H/btr06XCU5o3/9BkavadFUjdqXQ4TZCpAw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVh43H%2Fbtr06XCU5o3%2F9BkavadFUjdqXQ4TZCpAw1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1428&quot; height=&quot;1112&quot; data-origin-width=&quot;1428&quot; data-origin-height=&quot;1112&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;내부영역&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;순수 비즈니스 로직을 표현하는 기술 독립적인 영역&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;외부영역과 연계되는 포트를 가짐&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;외부영역&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;외부에서 들어오는 요청을 처리하는 &lt;/span&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;인바운드 어댑터&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;REST API 발행 컨트롤러&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;웹페이지 구성하는 스프링 MVC 컨트롤러&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;커맨드 핸들러&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;이벤트 메시지 구독 핸들러 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;비즈니스 로직에 의해 호출되어 외부와 연계되는 &lt;/span&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;아웃바운드 어댑터&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;DAO (데이터 액세스 처리)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;이벤트 메시지 발행 클래스&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;외부 서비스 호출 프락시&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;구조&lt;/span&gt;&lt;/h2&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;어댑터 -&amp;gt; 애플리케이션 &amp;gt; 도메인
이 의존성 흐름을 역행하면 안됨&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;도메인&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;레이어드 아키텍처에서의 도메인 레이어 개념과 같음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;애플리케이션의 핵심이 되는 도메인&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;애플리케이션&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;도메인을 이용한 애플리케이션의 비즈니스 로직 제공 : Service&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;포트를 가지고 있음 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;포트&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;헥사고날의 가장 큰 특징인 &lt;/span&gt;&lt;span&gt;고수준의 내부영역이 어댑터에 전혀 의존하지 않게 한다. 는는 &lt;b&gt;&lt;span&gt;포트&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt;를 통해 가능해짐&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;외부 어댑터를 끼울 수 있는 &lt;/span&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;인터페이스&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;인 바운드 포트&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;애플리케이션으로 흐름이 들어오는 포트&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;내부 영역 사용을 위해 표출된 API &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;외부영역의 인 바운드 어댑터가 호출 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;아웃 바운드 포트&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;애플리케이션에서 흐름이 나가는 포트&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;내부 영역이 외부 영역을 호출하는 방법을 정의 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;아웃바운드 어댑터가 아웃바운드 포트에 의존하여 구현됨 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;어댑터&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;애플리케이션 내에 있는 포트에 끼울 수 있는 구현체 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;포트를 나타내는 &lt;/span&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;인터페이스를 상속받아 구현&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;예시&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;julia&quot;&gt;&lt;code&gt;src/
  adapter/ -- 애플리케이션 계층의 인커밍 포트를 호출하는 인커밍(웹)어댑터와 아웃고팅 포트에 대한 구현을 제공하는 아웃고잉(영속성) 어댑터를 포함
    inbound/
      web/
        SendMoneyController
        ...
 &amp;nbsp;  outbound/
 &amp;nbsp;    persistence/
 &amp;nbsp;      AccountUpaEntity
 &amp;nbsp;      AccountMapper
 &amp;nbsp;      AccountPersistenceAdapter
 &amp;nbsp;      ActivityJpaEntity
 &amp;nbsp;      I)ActivityRepository
 &amp;nbsp;      I)SpringDataAccountRepository
  application/ -- 도메인 모델에 대한 비즈니스 로직을 수행하는 서비스 계층 
    service/ 
      GetAccountBalanceService -- GetAccountBalanceQuery구현
      MoneyTransferProperties
      NoOpAccountLock
      SendMoneyService -- SendMoneyUseCase 구현
      ThreshholdExccededException
    port/
      inbound/
        I)GetAccountBalanceQuery
        SendMoneyCommand
        I)SendMoneyUseCase
      outbound/
        I)AccountLock
        I)LoadAccountPort
        I)UpdateAccountSatePort
  domain/ -- 도메인 모델이 속해있는 패키지
    Account
    Activity
    ActivityWindow
    Money
common/
  @PersistenceAdpter
  @Usecase
  @WebAdapter&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;498&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sgiCn/btr1dlcbgDR/ymeVpuaDuLPRk0uXRBrkj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sgiCn/btr1dlcbgDR/ymeVpuaDuLPRk0uXRBrkj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sgiCn/btr1dlcbgDR/ymeVpuaDuLPRk0uXRBrkj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsgiCn%2Fbtr1dlcbgDR%2FymeVpuaDuLPRk0uXRBrkj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;498&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;498&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;육각형 내부&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;도메인 엔티티&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;도메인 엔티티와 상호작용하는 유스케이스(서비스)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;모든 의존성은 코어(도메인)를 향함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;육각형 바깥&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;웹과 상호작용하는 웹 어댑터, 외부시스템 어댑터, 영속성 어댑터 등 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;driving adapter : 주도하는 어댑터. 왼쪽. 애플리케이션 코어를 호출&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;포트가 코어에 있는 유스케이스 클래스에 의해 구현되고 호출되는 인터페이스&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;driven adapter : 주도되는 어댑터. 오른쪽. 코어에 의해 호출됨 &lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;포트가 어댑터에 의해 구현이 되고 코어에 의해 호출되는 인터페이스 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;클린 아키텍처&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;헥사고날 아키텍처의 아이디어와 유사함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;878&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blKsqN/btr1dmWraBQ/nJJcykafO6Xk2Ex1RWCHl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blKsqN/btr1dmWraBQ/nJJcykafO6Xk2Ex1RWCHl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blKsqN/btr1dmWraBQ/nJJcykafO6Xk2Ex1RWCHl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblKsqN%2Fbtr1dmWraBQ%2FnJJcykafO6Xk2Ex1RWCHl1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1202&quot; height=&quot;878&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;878&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;엔티티&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;제일 중앙에 위치&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;유스케이스&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;엔티티를 감싸는 객체&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;자동화된 시스템을 사용하는 처리 절차를 기술 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;애플리케이션에 특화된 업무규칙을 표현하며 내부의 핵심 업무규칙을 호출하며 시스템을 사용하는 흐름을 담음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;엔티티(고수준)는 유스케이스(저수준)를 알게 하면 안 된다.&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;간단한 객체여야 하고 데이터베이스, 프레임워크등에 의존되어서는 안 되고 유스케이스 객체를 통해서만 엔티티를 조작해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;세부사항&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;유스케이스를 감싸고 있는 모든 영역&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;입출력장치, 저장소, 웹 시스템, 서버, 프레임워크, 통신프로토콜&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;DIP를 이용해 플러그인처럼 유연하게 처리해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;출처&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;sk c&amp;amp;c 마이크로서비스 내부 아키텍처 - 2회 : 클린 아키텍처와 헥사고널 아키텍처 &lt;a href=&quot;https://engineering-skcc.github.io/microservice%20inner%20achitecture/inner-architecture-2/&quot;&gt;https://engineering-skcc.github.io/microservice%20inner%20achitecture/inner-architecture-2/&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;github.io [소프트웨어 아키텍처 패턴] 헥사고날(hexagonal) 아키텍처 &lt;a href=&quot;https://6mini.github.io/software%20architecture%20pattern/2022/11/23/hexagonal-architecture/&quot;&gt;https://6mini.github.io/software%20architecture%20pattern/2022/11/23/hexagonal-architecture/&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;티스토리 [Clean Architecture] 육각형 아키텍처 (헥사고날 아키텍처) 패키지 구조 &lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://devkingdom.tistory.com/342&quot;&gt;https://devkingdom.tistory.com/342&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Backend/공부,개념</category>
      <author>지수쓰</author>
      <guid isPermaLink="true">https://w97ww.tistory.com/114</guid>
      <comments>https://w97ww.tistory.com/114#entry114comment</comments>
      <pubDate>Mon, 6 Mar 2023 01:43:35 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Spring Data</title>
      <link>https://w97ww.tistory.com/113</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 신규 프로젝트에 도입할 기술들을 정하고 있어서 개념정리할 겸 적어본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;JDBC (Java DataBase Connectivity)&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Java와 데이터베이스를 연결하기 위한 Java 표준 인터페이스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 다양한 DB미들웨어의 드라이버 제공, JVM기반에서 어디든지 사용 가능&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- class.forName(driver), driverManager.getConnection, con.prepareStatement, pstmt.executeQuery 그 완전 기본 형태&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 커넥션 종료시 일일이 close 해주어야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Spring JDBC&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 스프링에서 DB를 사용하기 위한 오리지널 디펜던시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- JDBC사용 시 자주 사용하는 객체와 코드를 클래스 화하였음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Connection 열고 닫기, Statement준비 실행, ResultSet반복처리, Transaction 처리 등&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- MVC패턴으로 구현할 때 DAO를 통해 DB에 접근, DataSource는 Spring 의존성주입으로 넣어줌&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이거 이후에 MyBatis 같은 게 나옴&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흐름 ) Java에서 DB를 연결하는 작업을 하려고 하는데 처음엔 JDBC -&amp;gt; Spring으로 옮겨가면서 Spring JDBC -&amp;gt; 관계형을 정의하기 어려운 단점 -&amp;gt; JPA 같은 게 나옴&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;JPA&lt;/b&gt;&lt;br /&gt;- Java Persistence API 자바에서 관계형 데이터베이스를 사용하기 위한 양식을 정의한 인터페이스&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 자바진영의 ORM 기술표준으로 애플리케이션과 JDBC 사이에서 동작&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - ORM : SQL코드 구현 없이 객체지향프로그래밍 방식으로 DB를 사용하는 것 (ex. Hibernate)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Hibernate&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- JPA의 구현체&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 장점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - JDBC에서 중복된 코드가 발생했던 것 + 관계형 데이터베이스 매핑 불편함을 줄여줌&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 객체 수정에 따른 SQL 불필요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 캐싱, 데이터 접근 추상화 등&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 단점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 실제 쿼리 작성에 비해 성능이 좋지 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - JPA의 엔티티 생명주기, 캐시, 쓰기 지연, 상속전략 등 잘 알고쓰지 않으면 더 안 좋을 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Spring Data JPA&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- JPA를 Spring framework에서 편하게 사용할 수 있도록 만들어놓은 모듈&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ex. @Entity, @Column 등으로 간단하게 엔티티를 정의하고 DDL을 수행하였음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Spring Data&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 스프링에서 일관된 DB접근을 위한 추상화된 모델을 제공하는 디펜던시&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 두 가지 베이스 1) ORM 표준 Hibernate 기반 JPA / 2) Java DB 커넥션 표준인 JDBC&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 레포지토리 패턴, 커스텀 객체 매핑 추상화, 도메인 기본 클래스 구현, 이벤트 상태 지원(생성, 마지막 변경), 등&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Spring Data Commons&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Spring Data xx기반 디펜던시들은 Spring Data Commons기반으로 되어있음&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Repository(=PK지정) / CrudRepository(=PK를 이용해서 데이터를 찾고 CRUD수행을 간단히 할 수 있도록 추상화시켜 놓음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- vs JPA ) 실제 JPA사용한다면 Entity 클래스를 만들고 객체를 생성해 영속성 콘텍스트에 데이터를 넣고 커밋을 수행 / Spring Data JPA는 이를 하나의 메서드에 추상화했기 때문에 한 번에 수행할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Spring Data JDBC&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 등장 배경&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - JPA의 좋은 기능들의 단점 -&amp;gt;&lt;b&gt; lazy loading 발생 시점을 명확하게 알 수 없어 원하지 않는 순간에 성능적인 영향을 초래&lt;/b&gt;할 수 있음. 객체 초기화 시 프락시 객체에 대한 오류처리가 필요함&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - JPA가 가진 기술들로 인해 사용하기 어려운 요소를 제거하고 가능한 CRUD 기본작업에 충실한 모듈이 Spring Data JDBC&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 일반적인 Spring JDBC랑 차이 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - JDBC -&amp;gt; 쿼리를 직접 입력하고 함수 형태를 직접 인터페이스로 구현해야 하는 단점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 이를 Spring Data Commons를 바탕으로 재구성한 것이 Spring Data JDBC&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 구조&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 기본적인 JDBC 영속방식을 따름 (App -&amp;gt; JDBC API -&amp;gt; JDBC Driver manager -&amp;gt; JDBC Driver -&amp;gt; DB)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - &lt;b&gt;구현&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;- CrudRepository를 상속받은 Repository 인터페이스 생성 필요 (Spring Data 기반이라 이거는 같음)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/b&gt;- 연관관계 JPA의 OneToMany 라면 여기서는 MappedCollection이라는 어노테이션 사용&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 데이터베이스의 버전관리기술을 사용하거나 수동으로 쿼리를 서버에 질의해서 DDL을 미리 실행시켜야 함 =&amp;gt; 우리는 디비 이미 생성된 상태니까 이건 괜찮을 듯&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&lt;b&gt; Spring Data JDBC에서는 단방향만 접근 허용됨. 깨끗하게 모듈화 된 도메인을 얻을 수 있음&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Spring Data JDBC의 ID전략&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Row에 해당하는 고윳값 ID를 생성해야 하는 경우&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 보통 Auto Increment사용하지만 직접 만들고 싶을 때 Spring Data Auditing과 호환되지 않는 경우가 있음 (우아한 테크 세미나 참고 &lt;a href=&quot;https://www.youtube.com/watch?v=cflK7FTGPlg)&quot;&gt;https://www.youtube.com/watch?v=cflK7FTGPlg)&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=cflK7FTGPlg&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/F3Tnh/hyRMz2qNcJ/dUEK8tZ7KsfczLJAWqQis0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=168_234_612_420&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/cflK7FTGPlg&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- JPA는 @GeneratedValue 사용하는데 JDBC에서는 이걸 지원하지 않음&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 설정에서 DB DDL에 auto_increment설정 넣어줌&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- save 할 때 ID값과 Persistable의 isNew에 따라 작동하는데 이거 때문에 Auditing이 원하는 대로 작동하지 않을 수 있어서 주의필요함&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;R2DBC란&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Reactive Relational Database Connectivity&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 관계형 DB + Reactive Programming (관계형 DB에 논블로킹 접근이 가능하도록)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://blog.neonkid.xyz/223&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://blog.neonkid.xyz/223&lt;/a&gt; [Spring boot] JDBC와 Spring JDBC 그리고 MyBatis&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://blog.neonkid.xyz/224&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://blog.neonkid.xyz/224&lt;/a&gt; [Spring Data] Hibernate, JPA 그리고 Spring Data JPA&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://blog.neonkid.xyz/274&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://blog.neonkid.xyz/274&lt;/a&gt; [Spring Data] Spring Data module&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://blog.neonkid.xyz/275&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://blog.neonkid.xyz/275&lt;/a&gt; [Spring Data] Spring Data JDBC를 이용한 DB연동&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://blog.neonkid.xyz/282&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://blog.neonkid.xyz/282&lt;/a&gt; [Spring Data] Spring Data JDBC를 이용한 다양한 ID전략과 수동 ID전략 구현시 주의점&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽어볼 만한 문서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이명현 님 강의자료 &lt;a href=&quot;https://docs.google.com/presentation/d/1E7Y_L8TO6ZRZfFjBO6f_GBZxzdV0Klw0XNuH1Kv4JWA/edit#slide=id.p61&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.google.com/presentation/d/1E7Y_L8TO6ZRZfFjBO6f_GBZxzdV0Klw0XNuH1Kv4JWA/edit#slide=id.p61&lt;/a&gt;&lt;/p&gt;</description>
      <category>Backend/공부,개념</category>
      <author>지수쓰</author>
      <guid isPermaLink="true">https://w97ww.tistory.com/113</guid>
      <comments>https://w97ww.tistory.com/113#entry113comment</comments>
      <pubDate>Mon, 27 Feb 2023 04:34:13 +0900</pubDate>
    </item>
  </channel>
</rss>