<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>지구 정복 일기장</title>
    <link>https://awesomemin.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sat, 13 Jun 2026 22:16:06 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>어썸min</managingEditor>
    <item>
      <title>인턴을 하려다 1년 계약직이 되어버렸다: 입사 전 불안 기록</title>
      <link>https://awesomemin.tistory.com/21</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이틀 뒤에 입사를 앞두고 있다.&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;겨우 2학년을 마친 내가 어쩌다가 갑자기 개발자로 취업을 하게 됐을까?&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;때는 기말고사를 앞두고 있던 작년 12월 초, 이번 겨울방학에는 인턴을 꼭 하고 싶다는 생각을 했다. 개발을 항상 혼자 공부했기 때문에 열심히는 했지만 맞는 방향인지 몰랐고, 실무 경험이 필요하다고 생각했다. 또, AI로 인해 신입 개발자 채용 시장이 안 좋아져서 최대한 빠르게 '신입 딱지'를 떼고 싶다는 생각도 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;12월 4일, 이메일 발송&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 일단 이력서를 만들어서 가장 일하고 싶은 회사 두 곳에 무작정 이메일을 보냈다. 심지어 한 회사에는 채용 담당자 이메일을 못 찾아서 고객센터 주소로 이력서를 보냈다. 사실은 답장이 올 거란 기대 자체를 안 했다. 애초에 확인을 할지도 의문이었다. 그래도 최대한 확률을 높이기 위해 메일 제목도 자극적으로 써서 보냈고, 연락이 오는 회사가 나올 때까지 수십 통, 수백 통은 메일을 보낼 작정이었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1572&quot; data-origin-height=&quot;156&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EsfZE/dJMcad13JK2/Mi1pZUF9JoTxvnX2ovIMKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EsfZE/dJMcad13JK2/Mi1pZUF9JoTxvnX2ovIMKK/img.png&quot; data-alt=&quot;도저히 확인하지 않고는 배길 수 없는 저 패기 넘치는 메일 제목을 보라&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EsfZE/dJMcad13JK2/Mi1pZUF9JoTxvnX2ovIMKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEsfZE%2FdJMcad13JK2%2FMi1pZUF9JoTxvnX2ovIMKK%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;1572&quot; height=&quot;156&quot; data-origin-width=&quot;1572&quot; data-origin-height=&quot;156&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;도저히 확인하지 않고는 배길 수 없는 저 패기 넘치는 메일 제목을 보라&lt;/figcaption&gt;
&lt;/figure&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;그런데 놀랍게도 두 회사에서 모두 답장이 왔다. 한 회사에서는 아쉽게도 인턴 TO가 없으니 인재풀에 넣어두었다가 나중에 인턴 TO가 생기면 연락을 준다고 했고, 다른 회사에서는 일단 CTO님과 커피챗을 해볼 것을 제안했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;12월 9일, 커피챗&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생각보다 빠르게 커피챗 일정이 잡혔고, 판교의 사무실로 찾아갔다. 너무 떨렸다. 일단 사무실이 너무 좋아서 놀랐고, CTO님 키가 엄청 크셔서 또 놀랐다.&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;처음엔 엄청 떨렸는데, 막상 대화를 시작하니 CTO님이 잘 이끌어주셔서 즐겁게 대화했다. 커피챗에서는 왜 이 회사에 연락을 하게 되었는지나, 그동안 내가 해온 일들에 대해 대화를 나눴다. 그리고 또 미리 준비해 간 질문 리스트를 비롯해 내가 회사에 궁금한 점을 이것저것 물어보는 시간을 가졌다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;12월 22일, 과제 전형&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확히 마지막 기말고사가 끝나고 학교가 종강을 하자마자 2주 간의 과제 전형이 시작됐다. 기말고사와 과제 전형이 겹치면 어떻게 하나 걱정이 많았는데 다행히 회사에서 일정 편의를 봐주어서 종강과 동시에 과제를 시작할 수 있게 됐다.&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;과제 기간 14일 동안은 피치 못할 일정이 있는 날을 제외하고는 아침에 눈을 떠서 밤에 잠들 때까지 과제만 했다. 밥도 컴퓨터 앞에서 먹었다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(special thanks to my mommy)&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1월 15일, 실무 면접&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무 면접은 회사 사무실에서 오후 6시부터 두 시간 조금 넘게 봤다. 나는 풀스택이라 프론트엔드 개발자 두 분, 백엔드 개발자 두 분이 들어오셨다. 초반에는 이력서 기반으로 질문을 받았고, 이후에는 본격적으로 진행했던 과제를 기반으로 여러 가지 질문을 받았다. 잘 대답한 것도 있지만, 내가 잘 모르는 부분에 대한 질문도 꽤 많이 받았던 것 같다. 잘 모르는 질문에는 최대한 논리적으로 &quot;~니까 ~하지 않을까요?&quot;라는 식으로 최대한 내가 생각하는 방식을 보여주려고 노력했다.&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;면접이 끝나니까 8시가 넘은 늦은 시간이었는데, 그때까지 나를 위해 퇴근하지 않아 주신 면접관분들과 HR 매니저님께 뭔가 미안한 마음이 들었다. 다음 날, 실무 면접 통과 소식을 들었다.&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;사실 실무 면접은 그렇게까지 잘 봤는지 모르겠는데, 아마 과제 전형을 제출할 때 문서화를 상세히 해둔 것이 합격에 도움이 된 것 같다. 또, 신입의 패기를 보인 것도 도움이 되지 않았을까 싶다. (면접 때, &quot;만약 방학이 얼마 안 남아 1달밖에 일을 못 한다면, 남들보다 2배로 일하면 되지 않겠느냐&quot;는 취지로 이야기를 했었다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1월 22일, 문화 면접&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CEO님, CTO님과 마지막 절차인 문화 면접을 봤다. 원래 다른 기업의 최종 문화 면접은 그냥 팔다리 잘 붙어있는지 확인하는 정도의 형식적인 절차인 경우가 많다고 해서 크게 걱정하지 않았었는데, 이 회사는 인터넷에서 찾아보니 문화 면접에서 떨어졌다는 후기가 꽤 보여서 조금 긴장됐다.&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;확실히 사지 이상 여부 확인 면접은 아니었고, 문화 면접도 1시간 정도 진행됐다. 실무 면접과 달리 문화 면접에서는 기술적인 질문보다는 일에 대한 가치관이나 커리어에 대한 질문이 많았는데, 이는 내가 평소에 자주 깊게 생각해 보던 주제였기 때문에 답변하는 데 어려움이 있지는 않았다. 두 번의 면접 모두 뭐랄까 면접이라기보다는 편하게 대화를 하고 온 기분이었다.&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;그리고, 두 분이 모두 나에게 &quot;개발자보다 PO가 더 잘 어울리는 것 같다&quot;는 말을 해주셨다. 사실 나도 스스로 PM/PO 직무가 잘 어울리는 것 같다는 생각을 하긴 했었지만, 남의 눈에도 그렇게 보인다는 것이 참 신기했다. 내가 나를 봐도 일반적으로 생각하는 개발자의 이미지에 비해 커뮤니케이션이나 적극성, 실행력에 강점이 있는 것 같아 보이긴 한다. 아직은 개발이 너무 재밌고 좋지만, 추후에 커리어를 전환하게 된다면 개발자 외의 PM/PO 직무를 배제할 필요는 없을 것 같다는 생각도 들었다.&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;사담으로, 문화 면접에서 수정하고 싶은 답변이 하나 있다. 만약 100억이 있으면 뭘 하고 싶냐고 물으셨는데, &quot;어차피 돈은 있을 대로 있으니 사업성 생각하지 않고 많은 사람들이 사용하는 유용한 소프트웨어를 만들고 싶다&quot;라고 답했다. 사실 평소 내 생각은 &quot;이제 100억은 모았으니 어떻게 1000억으로 점프할지 고민해 볼 것 같다&quot;에 가까웠는데, 의식적으로 생각과 다르게 말하려고 했던 것은 아니지만 무의식적으로 그렇게 답변이 나갔다. 뭔가 조금 건방져 보일 수 있을 것 같다는 걱정이 들었던 것 같다. 지금 다시 생각해 보면 원래 생각대로 답변하는 것이 더 좋았을 것 같다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;최종 합격 이후 입사 준비&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 원래는 겨울 방학 동안만 인턴을 하려고 했는데 채용 절차가 끝나고 보니 어느새 1월 말이었다. 회사와 나 사이에서 &quot;어차피 한 달만 일하는 것은 회사와 나 모두에게 얻을 것이 없다&quot;라는 공감대가 형성됐고, 1년 계약직으로 일을 해보자는 제안을 받았다. 나로서는 안 그래도 휴학을 많이 했던 참에 졸업이 더 늦어진다는 리스크가 있는 선택이었지만, 개인적으로 정말 일해보고 싶은 기업이었고 또 채용 절차를 진행하면서 회사에 대한 좋은 인상을 많이 받았기 때문에 고민 없이 수락했다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;합격을 한 뒤에는 아주 기뻤지만, 막상 이제 실무에 투입될 생각을 하니 아직도 모르는 게 너무 많은 것 같고 업무를 잘할 수 있을까 걱정이 됐다. 그래서 열심히 공부를 했지만, 공부를 하면 할수록 내가 모르는 게 더 많이 보이고 더 걱정이 되는 것 같다. 이런 걱정은 입사일이 다가올수록 조금씩 심해져서, &quot;내가 팀에 아무런 도움이 안 되면 어떡하지?&quot;, &quot;회사가 나를 뽑은 게 실수였다면 어떡하지?&quot;와 같은 생각도 들기 시작했다. 책임감 있는 성격이 나를 이런 상황에서 나태해지지 않고 끊임없이 노력할 수 있게 해주는 것은 맞지만, 과해지면 불필요한 걱정과 불안이 될 수도 있겠다는 생각을 했다.&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;&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;/p&gt;</description>
      <category>생각</category>
      <author>어썸min</author>
      <guid isPermaLink="true">https://awesomemin.tistory.com/21</guid>
      <comments>https://awesomemin.tistory.com/21#entry21comment</comments>
      <pubDate>Sun, 22 Feb 2026 19:49:30 +0900</pubDate>
    </item>
    <item>
      <title>포커 게임 플랫폼 사업을 위한 사고 실험</title>
      <link>https://awesomemin.tistory.com/20</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;1. 홀덤 펍&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;고등학생 때 처음 포커에 관심이 생기고, 집 근처 홀덤 펍에서 열리는 토너먼트에 참가했었다. 당시 3~4만원 정도의 참가비를 내고 참가했다가 운이 좋게 파이널 테이블까지 갔다. 최종 4위 정도 했던 것 같은데, 상품으로 이영호 선수의 싸인이 된 키보드와 마우스를 받았었다. 아마 이 경험이 본격적으로 포커에 관심을 가지는 계기가 된 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러나 포커에 대해 알게 될수록, 국내 홀덤 펍의 게임 수준은 기대 이하였다. 업장의 이익을 위해서는 회전율을 올려야 하기에 당연한 일이겠지만, 비정상적으로 짧게 설정된 게임 시간과 전략보다 운에 기대는 플레이어들의 성향이 맞물려 실력에 따라 승패가 결정되는 전략게임보다 확률에 의해 순위가 결정되는 '도박'에 가까운 행태를 보였다. 심지어 게임 참가비도 평균 1시간 플레이를 위해 3~4만원을 내야하는 등 비교적 비싼 편인데다, 현금성 시상을 하기 때문에 도박죄에 해당될 리스크도 있다. 실제로 최근 편법으로 도박 행위를 알선한 홀덤 펍이 다수 적발되고 있기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 친구들과 플레이&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;친구들에게 포커를 소개해주고 함께 플레이를 하기도 했다. 그러나, 제대로 플레이하기 위해서는 최소 4~5명은 모아야 하는데 그 자체가 쉽지 않았다. 적절한 장소를 찾거나 카드와 칩 등 용품을 구비하는 것도 쉽지 않은 일이었다. 친구 중 한 명은 딜러 역할을 겸해야 한다는 것도 단점이다. 결국 친구들과 포커를 치는 것은 오래 가지 못했다.&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;&amp;nbsp;딱 한 번, 불법 오프라인 포커를 쳐 본 적이 있다. 인터넷에서 비밀스러운 경로로 안내를 받아 입장한 불법 도박장은 아직 아무도 입주하지 않은 신축 오피스텔의 지하실에 있었다. 굉장히 쫄은 상태로 10만원을 들고 게임을 치러 갔던 나는 딱 봐도 불량하게 생긴 아저씨들 사이에서 조용히 10만원을 다 잃을 때까지 포커만 치다가 조용히 나왔다. 만약 돈을 따더라도 자리에서 일어나려고 하면 맞을까봐 굉장히 무서웠던 기억이 있다. 애초에 불법이기 때문에 이런 행동은 해서는 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 온라인 포커 (피망, 한게임, pokerstars 등)&lt;/b&gt;&lt;b&gt;&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이렇게 다양한 경로로 포커를 접하면서도 내가 원하는 1)불법성이 없으며 2)전략과 실력에 따라 결과가 결정될 수 있도록 충분한 플레이타임과 게임의 수준이 보장되고 3)합리적인 가격에 플레이할 수 있는 경로는 찾을 수 없었다.&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;b&gt;가설&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;가설 1. 여성보다는 남성이, 그리고 고지능자일수록 포커 게임에 관심을 가질 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가설 2. 포커 게임에 관심을 가지는 사람은, &lt;b&gt;현금성 보상이 없더라도 흥미를 위해 기꺼이 돈을 내고 포커 게임에 참가할 것이다.&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;실험&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;1. 남자 대학생을 주 고객층으로 하여 대학교 내에서 포커 게임을 연다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 참가비를 0원, 1,000원, 5,000원, 10,000원 등 다양하게 조정하며 게임 참가 수요를 비교한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 보상이 없을 때, 비현금성 보상(기프티콘 등)이 있을 때, 현금성 보상이 있을 때의 각 경우에 대해 게임 참가 수요를 비교한다.&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;위 실험을 진행하기 위해 나는 일주일 안으로 학교 내 강의실을 대여하고 실제로 포커 게임을 주최할 생각이다. 고객의 입장에서 아예 무료인 것과 1,000원이라도 지불하는 것은 거의 하늘과 땅 수준의 차이가 있기 때문에 참가 비용은 1,000원으로 하여 9명을 모집해서 게임 개최를 성공하는 것을 목표로 한다. 만약 참가자 모집이 안 돼서 첫 번째 게임 주최에 실패한다면 이 아이디어는 폐기될 것이고, 만약 성공한다면 주기적으로 게임을 열어 고객을 획득하기 위한 최적의 두 변수(비용과 보상)를 확인할 것이다.&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;b&gt;사업화 계획&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;1. 서울의 주요 대학으로 확장하여 운영한다. 또는 대학생에서 확장해 일반인을 대상으로 운영한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 어플 등을 개발해 '내 전적' 및 '랭크 시스템'을 도입해 현금성 보상 없이도 게임에 지속적으로 참가할 유인을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 포커 외의 보드게임 및 다양한 활동을 주선하는 플랫폼으로 확장한다.&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;b&gt;한계점&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;1. 시장이 작다. 2024년 기준 서울 주요 10개 대학의 재학생 수는 약 23만 명이다. 이 중 한 달에 1회 이상 이 서비스를 이용하는 비중을 낙관적으로 2%로 설정하면 MAU가 4,600명이고, 고객이 1회 서비스 이용 시 지불하는 비용을 5,000원으로 설정하였을 때 한 달에 기대 가능한 매출은 2,300만원이다. 공간 대여비, 인건비, 기타운영비 등을 고려하면 영업이익은 그보다 훨씬 낮은 수치일 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 법률적 리스크가 있다. 물론 아직 실험을 거치지 않았기 때문에 가설에 대해 임의로 판단할 수는 없지만, 아무런 보상 없이 순수한 흥미를 위해 게임에 참가하는 사람의 비중이 높지 않을 것으로 예상하고 있다. 따라서 일정량의 보상을 지급해야 할 것으로 생각하고 있으며 이는 도박죄에 해당할 염려가 있다.&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;사실 본 아이디어는 '플랩풋볼'에서 영감을 얻었다. 플랩풋볼은 풋살이 하고 싶은 사람들에게 1만원 내외의 요금을 받고 인원을 모아 경기장, 심판 등의 서비스를 제공해주는 풋살 매칭 서비스이다. 위 아이디어가 잘 정착한다면 포커를 비롯한 '마인드 스포츠'계의 플랩풋볼이 될 수 있지 않을까?&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;</description>
      <category>프로젝트</category>
      <author>어썸min</author>
      <guid isPermaLink="true">https://awesomemin.tistory.com/20</guid>
      <comments>https://awesomemin.tistory.com/20#entry20comment</comments>
      <pubDate>Fri, 28 Mar 2025 13:44:39 +0900</pubDate>
    </item>
    <item>
      <title>[프로젝트 회고] 내 비밀을 눌러봐(Click My Secret)</title>
      <link>https://awesomemin.tistory.com/19</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이번 크리스마스, 가장 많이 클릭한 친구에게만 내 비밀을 공유해 보세요&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blog.png&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;767&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oYnbB/btsLz2DWBNC/cVSw1dLoxKBa7RkNsoTupk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oYnbB/btsLz2DWBNC/cVSw1dLoxKBa7RkNsoTupk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oYnbB/btsLz2DWBNC/cVSw1dLoxKBa7RkNsoTupk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoYnbB%2FbtsLz2DWBNC%2FcVSw1dLoxKBa7RkNsoTupk%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;750&quot; height=&quot;767&quot; data-filename=&quot;blog.png&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;767&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/awesomemin/click-my-secret&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;깃허브&lt;/a&gt;&amp;nbsp; &lt;a title=&quot;프로젝트 페이지&quot; href=&quot;https://clickmysecret.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;프로젝트 페이지&lt;/a&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;/b&gt; : 2024. 11. 28. ~ 2024. 12. 8. (11일)&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;a href=&quot;https://colormytree.me/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;【내 트리를 꾸며줘】&lt;/a&gt; 서비스에서 영감을 받아 비슷한 이벤트성 서비스를 만들고 싶었습니다. 2년 전 만들었던 &lt;a href=&quot;https://awesomemin.tistory.com/9&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;클릭 배틀&lt;/a&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;/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;ID / PW / 닉네임 입력으로 회원가입&lt;/li&gt;
&lt;li&gt;1인당 비밀 1개 생성 가능&lt;/li&gt;
&lt;li&gt;비밀 페이지 내 열쇠 버튼( ) 클릭 수에 따른 실시간 순위 확인&lt;/li&gt;
&lt;li&gt;2024년 12월 24일 22:00 기준 클릭 수 상위 N명에게만 비밀이 공개됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;회원가입 51건&lt;/li&gt;
&lt;li&gt;비밀 생성 28건&lt;/li&gt;
&lt;li&gt;총 클릭 수 22,006회&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 이번 프로젝트는 &lt;i&gt;'비즈니스적 실패, 기술적 성공'으로&lt;/i&gt; 정의할 수 있을 것 같습니다. 예상보다 훨씬 적은 유저밖에 모으지 못했지만 기술적으로는 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(2년 전 클릭 배틀에 비교도 할 수 없을 정도로)&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;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;&amp;nbsp;비즈니스의 성공을 위해서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;'내가 만들고 싶은 것'이 아니라 '사람들이 원하는 것'&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;을 만들어야 합니다. 그리고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;시장에 제품을 내놓기 전까지는 사람들이 그것을 원할지 절대 알 수가 없기 때문에 핵심 기능만 가진 MVP를 빠른 주기로 만들어서 시장의 반응을 살피면서 서비스를 꾸준히 개선해 나가야 합니다.&amp;nbsp;&lt;/b&gt;위와 같은 이유로 2주도 안 되는 짧은 기간 동안 혼자서 기획, 디자인, 개발, 배포까지 마치고 빠르게 출시를 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;처음 기획을 할 때는 클릭배틀처럼 별도의 마케팅 없이도 자연스럽게 바이럴이 되면서 유저가 꾸준히 유입될 수 있는 서비스를 상상했습니다. 하지만 몇 가지 설계 실수로 인해 제가 생각한 &lt;b&gt;자동 바이럴 엔진&lt;/b&gt;의 시동이 잘 걸리지 않았습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;유저에게 뭔가를 요구하기 전에 먼저 가치를 제공해야 한다.&lt;/b&gt;&lt;/h4&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;&amp;nbsp;예를 들어, &lt;b&gt;비밀 입력을 먼저 유도하고 비밀 완성 후에 공유를 위해 회원 정보를 입력하는 방법&lt;/b&gt;이 있을 것입니다. 이런 방법을 사용하면 유저에게 비밀 입력에 소요된 시간과 노력이라는&amp;nbsp;&lt;b&gt;매몰 비용을 발생시켜 쉽게 서비스를 이탈하지 못하게 하고 전환율을 높일 수 있을 것&lt;/b&gt;입니다. 비밀 생성뿐만 아니라, 비밀 클릭에 있어서도 &lt;b&gt;비로그인 상태에서 일단 버튼 클릭을 허용하고 클릭 점수를 저장하고 싶다면 로그인&lt;/b&gt;을 할 수 있도록 구현하면 좋았을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;또한, 자연스러운 가입 절차를 제공하기 위해서는 &lt;b&gt;회원가입 페이지로 유저를 이동시키지 않고 비밀 생성 페이지에서 회원가입을 위한 정보를 추가로 입력하도록 하는 방법&lt;/b&gt;도 효과가 있을 것으로 생각합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;사용자를 당황시키거나 고민하게 만들지 마라.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;출시 이후 주요 반응은 &quot;재미있는 컨셉이다&quot;라는 긍정적 평가도 있었지만 동시에 서비스의 핵심 기능에 대한 의문도 있었습니다. 사용자 입장에서는 덜컥 &lt;b&gt;비밀을 만들라는 요청을 받았을 때, 무엇을 입력해야 할지 고민이 된다는 것&lt;/b&gt;입니다. 서비스 특성 상 &lt;b&gt;누가 나의 비밀을 보게 될지도 모르는데 무작정 민감한 정보를 입력하기 어렵다&lt;/b&gt;는 의견이 있었습니다. 또, &lt;b&gt;사람들이 과연 자신의 비밀을 궁금해할지에 대한 고민&lt;/b&gt;을 하는 사용자도 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이러한 반응이 나온 데에는 잘못된 단어 선택도 기여했다고 생각합니다. 처음 기획 의도는 &lt;b&gt;본인의 민감한 비밀이 아니라 그저 재미있는 이야기를 주고받을 수 있는 가벼운 서비스가 되는 것&lt;/b&gt;이었습니다. 그러나, &lt;b&gt;비밀이라는 단어가 주는 느낌&lt;/b&gt;이 사용자로 하여금 무겁고 민감한 이야기를 해야 할 것 같다는 생각을 심어주었던 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 문제를 개선하기 위해서 비밀 생성 과정에서 &lt;b&gt;사용자에게 더 친절한 가이드를 제공&lt;/b&gt;했다면 좋았을 것 같습니다. &lt;i&gt;&quot;무엇을 입력할지 고민되시나요?&quot;&lt;/i&gt;같은 툴팁을 배치해서, 이러한 내용을 입력해보라는 추천이나 예시를 보여줬다면 &lt;b&gt;사용자의 서비스 이해도를 높이고 개발자의 기획 의도를 더 잘 전달할 수 있었을 것&lt;/b&gt;입니다.&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;기술적으로 배운 점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;figma : 기획 및 UI 디자인&lt;/li&gt;
&lt;li&gt;next.js : 풀스택 개발 (SSR 및 비즈니스 로직, API 엔드포인트)&lt;/li&gt;
&lt;li&gt;tailwind : 스타일링&lt;/li&gt;
&lt;li&gt;prisma : ORM&lt;/li&gt;
&lt;li&gt; PostgreSQL : 메인 DB&lt;/li&gt;
&lt;li&gt;Redis : 클릭 수 저장을 위한 캐시 DB&lt;/li&gt;
&lt;li&gt;docker : 배포 환경 구축&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2년 전 프로젝트의 문제점을 Redis로 해결하다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;2년 전 프로젝트인 클릭 배틀에서 기술적으로 가장 큰 문제였던 부분이 바로 &lt;b&gt;과도한 클릭에 따른 서버 하드웨어 자원의 과부하&lt;/b&gt;였습니다. 당시에는 컴퓨터 구조나 서버의 작동 방식에 대한 지식이 거의 없었기 때문에, 클릭 1회 당 하드디스크에 쓰기를 1회 실행하는 클라이언트 요청이 서버에 어느 정도의 부담을 줄지 예상하지 못했습니다. 또한, 해당 문제의 근본 원인을 파악하지 못한 채 임시방편으로 초당 요청 횟수를 제한하는 방법으로 대처했고 그에 따른 부작용이 발생하기도 했습니다. &lt;a href=&quot;https://awesomemin.tistory.com/9&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(해당 프로젝트 회고 글의 TroubleShooting 문단 참고)&lt;/a&gt; 그러나 군대에서 컴퓨터 과학에 대해 공부하면서&amp;nbsp;&lt;b&gt;하드디스크 입출력은 메모리 입출력에 비해 훨씬 큰 비용이 필요하다&lt;/b&gt;는 것을 알았습니다. 따라서 이번 프로젝트에서는 &lt;b&gt;in-memory DB인 Redis를 도입하여 광클 트래픽에 대응&lt;/b&gt;하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Redis를 도입하는 과정에서도 고민은 있었습니다. 클릭 데이터 전체를 Redis에 저장해서 cache DB로 사용할지, Redis를 일종의 큐로 사용해서 신규 클릭 데이터만 저장하고 주기적으로 DB에 적용하며 Redis의 정보를 삭제할지 선택해야 했습니다. cache DB로 사용할 때에는 PostgreSQL과 Redis 간의 동기화에 주의해야 한다는 점이 있었지만 큐로 사용하는 방안에 비해 구현이 쉬울 것이라&amp;nbsp;판단해 &lt;b&gt;Redis를 클릭 데이터의 cache DB로 쓰고 대신 일정 시간마다 PostgreSQL에 백업하는 방식을 선택&lt;/b&gt;했습니다. (사실 kafka라는 기술을 사용하면 대량의 클릭을 메시지 큐를 이용해 안정적으로 처리할 수 있다는 것 같았으나 그것을 또 학습하고 적용하기에는 너무 오버엔지니어링인 것 같아 포기했습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그렇게 Redis를 도입하여 클릭 데이터를 읽고 기록하는 로직을 구현했으나... Redis 데이터를 PostgreSQL로 백업하는 로직 구현 과정 중에 문제가 발생했습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;next.js가 서버리스라고??&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;기본적으로 클릭 데이터에 대한 쓰기와 읽기 요청은 Redis로 처리하고, 데이터의 장기 저장을 위해 10분마다 PostgreSQL에 백업하는 로직을 작성하려고 했습니다. 이렇게 하면 만약 Redis 세션이 종료되거나 서버가 재부팅되더라도 PostgreSQL에 저장되어 있는 클릭 데이터를 Redis로 불러오기만 하면 데이터 손실 없이 서비스를 지속적으로 운영할 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그런데, 해당 로직을 작성하려고 살펴보니 &lt;b&gt;코드를 어디에 작성해야 할 지 알 수가 없었습니다.&lt;/b&gt;  예를 들어 자바스크립트 백엔드 라이브러리인 express.js에서는 app.js라는 몸통이 되는 파일에 express 서버 인스턴스를 생성하고, 거기에 미들웨어나 라우터를 붙여서 서버를 만듭니다. 그러나 &lt;b&gt;next.js에는 express.js의 app.js와 같은 서버의 중심이 되는 파일이 없었습니다.&amp;nbsp;&lt;/b&gt;그래서 setInterval을 사용해서 DB 백업을 실행하는 로직을 어디에 어떻게 작성해야 할지 혼란스러웠습니다. next.js의 미들웨어나 라우트 핸들러는 클라이언트의 요청이 있을 때만 실행되기 때문에 서버가 지속적으로 동작하는 작업을 처리하기에는 구조적으로 적합하지 않았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이쯤 되니 next.js가 무엇인지 정확히 모르고 사용하고 있었다는 생각이 들었습니다. 어렴풋하게 &lt;b&gt;'SSR을 지원하는 풀스택 리액트 프레임워크'&lt;/b&gt; 정도로 알고 있었던 것 같습니다. 구글에 검색하거나 GPT에게 물어봐도 next.js가 무엇인지 명쾌하게 이해시켜주는 내용은 없었습니다. 그저 SSR, 리액트, 풀스택 프레임워크 같은 단어들이 계속해서 등장하면서 어느 정도 느낌만 잡히는 기분이었습니다. 결국 제 나름대로 정보를 찾아보고 공부를 하면서 next.js에 대해 다음과 같이 정의를 하였습니다. &quot;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;next.js는 서버 사이드 렌더링을 하기 위한 백엔드 기능을 지원하는 서버리스 환경에 최적화된 리액트 프레임워크이다.&quot; &lt;/span&gt;&lt;/b&gt;즉, next.js에서 제공하는 라우트 핸들러는 요청이 발생할 때만 실행되기 때문에 서버리스에 최적화되어 있어서 setInterval 등을 사용해서 주기적으로 특정 행위를 반복 실행하도록 하는 것은 부적절하다는 것을 알게 되었습니다. 따라서 아래와 같이 &lt;b&gt;미들웨어에 10분이 경과했는지 확인하는 로직을 넣고 Redis 동기화 전용 API 엔드포인트를 만들어서 구현하였습니다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1735216388319&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// middleware.ts
let lastRedisSync = new Date(); // 마지막으로 Redis를 동기화 한 시간

export async function middleware(request: NextRequest) {
  // 마지막 동기화 시간이 10분 경과됐으면 동기화 API를 호출
  if (Date.now() - lastRedisSync.getTime() &amp;gt;= 10 * 60 * 1000) {
    fetch(`${serverAddress}/api/redisSync`);
    lastRedisSync = new Date();
  }
  // 중략...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;해당 경험을 통해 &lt;b&gt;next.js의 백엔드 기능은 근본적으로 SSR을 지원하기 위한 용도이고, 비즈니스 로직이 간단한 어플리케이션에 경우에 한정하여 next.js의 API 라우터에 비즈니스 로직을 추가하여 풀스택 프레임워크로 사용할 수 있다&lt;/b&gt;는 나름의 결론을 내렸습니다. 그래서 다음 프로젝트에는 next.js를 SSR을 위한 용도로만 사용하고 백엔드 비즈니스 로직은 별도의 nest.js 서버로 분리할 예정입니다.&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;앞으로의 과제와 총평&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;모니터링 가능한 환경 구축을 위한 학습.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;프로젝트를 할 때마다 Google Analytics를 사이트에 붙이긴 하지만 사용법을 잘 몰라 겨우 활성 사용자 수를 확인하는 정도에 불과하고, 제대로 된 로깅이나 모니터링 도구도 없어 서비스에 문제가 발생해도 실시간으로 확인하고 대처할 수가 없었습니다. 위의 '&lt;i&gt;비즈니스적으로 배운 점' &lt;/i&gt;파트에서도 회원가입 과정에서 유저가 많이 이탈했다는 식의 이야기를 적었는데 사실 뇌피셜일 뿐이고 정확한 이탈율, 전환율 등의 지표를 확인할 수는 없었습니다. &lt;b&gt;일을 과학적으로 잘 하기 위해서는 숫자로 이야기해야 합니다.&amp;nbsp;&lt;/b&gt;지금까지는 초보 개발자로서 당장의 기능을 구현하는 것에 집중하느라 배포 이후의 운영에 대한 대비가 잘 되어있지 않았습니다. 다음 프로젝트부터는 &lt;b&gt;모니터링 및 분석 툴에 대한 학습을 통해 사용자의 행동에 대한 정확한 데이터를 가지고 서비스를 발전&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;생각보다 많은 유저를 모으거나 큰 광고 수익을 얻지는 못했지만 재미있는 프로젝트였습니다. 디자인적, 기술적으로도 지금껏 진행한 모든 프로젝트 중 가장 완성도가 높았다고 생각합니다. 다음 프로젝트는 한동안 개인적인 공부 후에 진행해서 비즈니스적, 디자인적, 기술적으로 모두 이번 프로젝트보다 월등히 뛰어난 결과물을 내놓겠습니다.&lt;/p&gt;</description>
      <category>프로젝트</category>
      <category>프로젝트</category>
      <category>프로젝트회고</category>
      <category>회고</category>
      <author>어썸min</author>
      <guid isPermaLink="true">https://awesomemin.tistory.com/19</guid>
      <comments>https://awesomemin.tistory.com/19#entry19comment</comments>
      <pubDate>Thu, 26 Dec 2024 21:58:05 +0900</pubDate>
    </item>
    <item>
      <title>백준 1914 : 하노이 탑 (C++) / C++에서 long long보다 큰 정수를 다루는 법</title>
      <link>https://awesomemin.tistory.com/18</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;※ 주의 : 이 글은 재귀를 이용한 하노이 탑 해결 알고리즘이 아닌, C++에서 자료형의 범위를 넘어가는 큰 수를 다루는 방법을 중점적으로 다루고 있습니다. 재귀를 이용한 하노이 탑 해결 알고리즘이 궁금하다면 다른 블로그의 글을 참고하시기 바랍니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;a title=&quot;문제 페이지&quot; href=&quot;https://www.acmicpc.net/problem/1914&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;세 개의 장대가 있고 첫 번째 장대에는 반경이 서로 다른 n개의 원판이 쌓여 있다. 각 원판은 반경이 큰 수서대로 쌓여있다. 이제 수도승들이 다음 규칙에 따라 첫 번째 장대에서 세 번째 장대로 옮기려 한다.&lt;br /&gt;1. 한 번에 한 개의 원판만을 다른 탑으로 옮길 수 있다.&lt;br /&gt;2. 쌓아 놓은 원판은 항상 위의 것이 아래의 것보다 작아야 한다.&lt;br /&gt;이 작업을 수행하는데 필요한 이동 순서를 출력하는 프로그램을 작성하라. 단, 이동 횟수는 최소가 되어야 한다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;입력&lt;/b&gt;&lt;br /&gt;첫째 줄에 첫 번째 장대에 쌓인 원판의 개수 N(1 &amp;le; N &amp;le; 100)이 주어진다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;출력&lt;/b&gt;&lt;br /&gt;첫째 줄에 옮긴 횟수 K를 출력한다.&lt;br /&gt;N이 20 이하인 입력에 대해서는 두 번째 줄부터 수행 과정을 출력한다. 두 번째 줄부터 K개의 줄에 걸쳐 두 정수 A B를 빈칸을 사이에 두고 출력하는데, 이는 A번째 탑의 가장 위에 있는 원판을 B번째 탑의 가장 위로 옮긴다는 뜻이다. N이 20보다 큰 경우에는 과정은 출력할 필요가 없다.&lt;/blockquote&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;pre id=&quot;code_1711165867702&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

void Hanoi(int n, int from, int drop, int to) {
	if(n == 1) {
		cout&amp;lt;&amp;lt;from&amp;lt;&amp;lt;&quot; &quot;&amp;lt;&amp;lt;to&amp;lt;&amp;lt;&quot;\n&quot;;
		return;
	}
	
	Hanoi(n - 1, from, to, drop);
	cout&amp;lt;&amp;lt;from&amp;lt;&amp;lt;&quot; &quot;&amp;lt;&amp;lt;to&amp;lt;&amp;lt;&quot;\n&quot;;
	Hanoi(n - 1, drop, from, to);
}

int main(void) {
	int N; cin&amp;gt;&amp;gt;N;
	
	int count = 0;
	for(int i = 0; i &amp;lt; N; i++) {
		count = (count * 2) + 1;
	}
	cout&amp;lt;&amp;lt;count&amp;lt;&amp;lt;&quot;\n&quot;;
	
	if(N &amp;lt;= 20) {
		Hanoi(N, 1, 2, 3);
	}
	return 0;
}&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;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;입력 1: 30&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #666666;&quot;&gt;출력 1 : 1073741823&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #666666;&quot;&gt;입력 2 : 31&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #666666;&quot;&gt;출력 2 : 2147483647&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #666666;&quot;&gt;입력 3 : 32&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #666666;&quot;&gt;출력 3 : &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;-1&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력을 담는 count 변수의 자료형이 int형이기 때문에 너무 큰 출력값을 저장하지 못하고 오버플로우가 발생한 모습이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;int형의 범위 : -2&lt;sup&gt;31&lt;/sup&gt; ~ (2&lt;sup&gt;31&lt;/sup&gt; - 1) (-2,147,483,648 ~ 2,147,483,647)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해 count 변수의 자료형을 더 큰 숫자를 담을 수 있는 long long 자료형으로 바꾸어 실행해본다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;입력 1 : 32&lt;br /&gt;출력 1 : 4294967295&lt;br /&gt;&lt;br /&gt;입력 2 : 62&lt;br /&gt;출력 2 : 4611686018427387903&lt;br /&gt;&lt;br /&gt;입력 3 : 63&lt;br /&gt;출력 3 : 9223372036854775807&lt;br /&gt;&lt;br /&gt;입력 4 : 64&lt;br /&gt;출력 4 : &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;-1&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&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;blockquote data-ke-style=&quot;style3&quot;&gt;long long형의 범위 : -2&lt;sup&gt;63&lt;/sup&gt; ~ (2&lt;sup&gt;63&lt;/sup&gt; - 1) (-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 잘 생각해 보니 count 변수는 실행된 횟수를 담는 변수이기 때문에 음수가 될 일이 없다. long long 자료형은 부호가 있는 자료형이기 때문에 우리의 소중한 메모리의 절반을 사용될 일 없는 음수를 표현하는 데 사용하고 있는 것이다. 따라서 마지막으로 우리가 알고 있는 부호 없는 정수 자료형 중 가장 큰 unsigned long long을 사용하여 실행해 본다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;입력 1 : 64&lt;br /&gt;출력 1 : 18446744073709551615&lt;br /&gt;&lt;br /&gt;입력 2 : 65&lt;br /&gt;출력 2 : &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;18446744073709551615&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;입력 3 : 66&lt;br /&gt;출력 3 : &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;18446744073709551615&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;입력 4 : 100&lt;br /&gt;출력 4 : &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;18446744073709551615&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&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;blockquote data-ke-style=&quot;style3&quot;&gt;unsigned long long형의 범위 : 0 ~ 2&lt;sup&gt;64&lt;/sup&gt; (0 ~ 18,446,744,073,709,551,615)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 생각한 방법은 이 수를 벡터에 담는 것이다. 예를 들어 정수 147,573,952,589,676,412,927를 표현하고 싶다면 다음과 같이 저장하는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1711169032889&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vector&amp;lt;int&amp;gt; count = { 1, 4, 7, 5, 7, 3, 9, 5, 2, 5, 8, 9, 6, 7, 6, 4, 1, 2, 9, 2, 7 };&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;위의 방법을 사용하면, 벡터의 원소 개수는 메모리가 허용하는 한 무제한 늘어날 수 있기 때문에 아무리 큰 정수라도 모두 표현할 수 있다. 그러나 내가 작성한 알고리즘에서는 입력이 1 늘어날 때마다 count에 2를 곱하고 1을 더해야 하기 때문에 이 연산을 위한 별도의 코드를 작성하여야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1711170297023&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//변경 전
int count = 0;
for(int i = 0; i &amp;lt; N; i++) {
	count = (count * 2) + 1;
}
cout&amp;lt;&amp;lt;count&amp;lt;&amp;lt;&quot;\n&quot;;

//변경 후
vector&amp;lt;int&amp;gt; count = {0};
for(int i = 0; i &amp;lt; N; i++) {
	mul2(count);
	plus1(count);
}
printCount(count);&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;일단 위와 같이 일단 벡터를 사용해 count의 값을 저장해 준다. 그리고 벡터로 표현된 정수의 계산을 위한 mul2 함수와 plus1 함수를 따로 구현하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1711170436974&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void mul2(vector&amp;lt;int&amp;gt; &amp;amp;count) {
	int carry = 0;
	for(int digit = count.size() - 1; digit &amp;gt;= 0; digit--) {
		count[digit] = (count[digit] * 2) + carry;
		carry = 0;
		
		if(digit == 0 &amp;amp;&amp;amp; count[digit] &amp;gt;= 10) {
			count[digit] = count[digit] % 10;
			count.insert(count.begin(), 1);
		} else if(count[digit] &amp;gt;= 10) {
			count[digit] = count[digit] % 10;
			carry = 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;mul2 함수의 동작은 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;1) 맨 마지막 자리에서부터 2를 곱한다.&lt;br /&gt;&amp;nbsp; 1-1) 만약 뒷자리에서 올림값(carry)이 존재했다면 이를 더해준다.&lt;br /&gt;&amp;nbsp; 1-2) 올림값을 더한 뒤에는 올림값을 0으로 초기화해 준다.&lt;br /&gt;2) 만약 2를 곱한 값이 10을 넘지 않는다면 다음 자릿수로 넘어가 반복한다.&lt;br /&gt;3) 만약 2를 곱한 값이 10을 넘는다면 일의 자릿수만 취하고, 나머지 값은 다음 자릿수에 올림값으로 넘긴다.&lt;br /&gt;&amp;nbsp; 3-1) 만약 맨 앞자리 수가 10이 넘어 더 이상 넘길 다음 자릿수가 없다면, 일의 자릿수만 취하고 올림값을 앞에 새 원소로 삽입한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시를 통해 mul2 함수의 동작을 더 자세히 알아보자.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-ke-style=&quot;style3&quot;&gt;mul2( { 9, 8, 7, 2 } );&lt;br /&gt;9872에 2를 곱하는 예시이다.&lt;/li&gt;
&lt;li data-ke-style=&quot;style3&quot;&gt;{ 9, 8, 7, &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;4&lt;/b&gt;&lt;/span&gt; }&lt;br /&gt;맨 마지막 자리에 2를 곱한다. 값이 10을 넘지 않았으므로 다음 자리로 넘어간다.&lt;/li&gt;
&lt;li data-ke-style=&quot;style3&quot;&gt;{ 9, 8, &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;14&lt;/b&gt;&lt;/span&gt;, 4 }&lt;br /&gt;세 번째 자리에 2를 곱한다. 값이 10을 넘었다.&lt;/li&gt;
&lt;li data-ke-style=&quot;style3&quot;&gt;{ 9, 8 &lt;span style=&quot;color: #ef5369;&quot;&gt;(&lt;b&gt;+ 1)&lt;/b&gt;&lt;/span&gt;, &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;4&lt;/b&gt;&lt;/span&gt;, 4 }&lt;br /&gt;일의 자릿수만 취하고, 나머지 값은 두 번째 자리에 올림값으로 넘겼다.&lt;/li&gt;
&lt;li data-ke-style=&quot;style3&quot;&gt;{ 9, &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;16&lt;/b&gt;&lt;/span&gt; (+ 1), 4, 4 }&lt;br /&gt;두 번째 자리에 2를 곱한다.&lt;/li&gt;
&lt;li data-ke-style=&quot;style3&quot;&gt;{ 9, &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;17&lt;/b&gt;&lt;/span&gt;, 4, 4 }&lt;br /&gt;뒷자리에서 올라온 올림값을 더해준다. 값이 10을 넘었다.&lt;/li&gt;
&lt;li data-ke-style=&quot;style3&quot;&gt;{ 9 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;(+ 1)&lt;/span&gt;&lt;/b&gt;, &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;7&lt;/b&gt;&lt;/span&gt;, 4, 4 }&lt;br /&gt;일의 자릿수만 취하고, 나머지 값은 첫 번째 자리에 올림값으로 넘겨준다.&lt;/li&gt;
&lt;li data-ke-style=&quot;style3&quot;&gt;{ &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;19&lt;/b&gt;&lt;/span&gt;, 7, 4, 4 }&lt;br /&gt;첫 번째 자리에 2를 곱하고 올림수를 더한다. 값이 10을 넘었고, 넘길 다음 자리가 없다.&lt;/li&gt;
&lt;li data-ke-style=&quot;style3&quot;&gt;{ &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1&lt;/b&gt;&lt;/span&gt;, &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;9&lt;/b&gt;&lt;/span&gt;, 7, 4, 4 }&lt;br /&gt;일의 자릿수만 취하고, 올림값을 앞에 새 원소로 삽입한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;답이 맞는지 확인해 보자. (9872 x 2 = 19744) 완벽하다! 이렇게 벡터로 표현된 정수에 2를 곱하는 함수를 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 비슷한 방법으로 구현한 plus1 함수이다. 직접 함수의 동작을 분석해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1711171914318&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void plus1(vector&amp;lt;int&amp;gt; &amp;amp;count) {
	int carry = 0;
	for(int digit = count.size() - 1; digit &amp;gt;= 0; digit--) {
		count[digit] = count[digit] + 1 + carry;
		carry = 0;
		
		if(digit == 0 &amp;amp;&amp;amp; count[digit] &amp;gt;= 10) {
			count[digit] = count[digit] % 10;
			count.insert(count.begin(), 1);
		} else if(count[digit] &amp;gt;= 10) {
			count[digit] = count[digit] % 10;
			carry = 1;
		} else {
			break;
		}
	}
}&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;/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;plus1 함수의 동작을 직접 분석해 보았는가? 그렇다면 마지막으로 작동하는 전체 정답 코드를 보이겠다.&lt;/p&gt;
&lt;pre id=&quot;code_1711172140665&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
using namespace std;

void Hanoi(int n, int from, int drop, int to) {
	if(n == 1) {
		cout&amp;lt;&amp;lt;from&amp;lt;&amp;lt;&quot; &quot;&amp;lt;&amp;lt;to&amp;lt;&amp;lt;&quot;\n&quot;;
		return;
	}
	
	Hanoi(n - 1, from, to, drop);
	cout&amp;lt;&amp;lt;from&amp;lt;&amp;lt;&quot; &quot;&amp;lt;&amp;lt;to&amp;lt;&amp;lt;&quot;\n&quot;;
	Hanoi(n - 1, drop, from, to);
}

void mul2(vector&amp;lt;int&amp;gt; &amp;amp;count) {
	int carry = 0;
	for(int digit = count.size() - 1; digit &amp;gt;= 0; digit--) {
		count[digit] = (count[digit] * 2) + carry;
		carry = 0;
		
		if(digit == 0 &amp;amp;&amp;amp; count[digit] &amp;gt;= 10) {
			count[digit] = count[digit] % 10;
			count.insert(count.begin(), 1);
		} else if(count[digit] &amp;gt;= 10) {
			count[digit] = count[digit] % 10;
			carry = 1;
		}
	}
}

void plus1(vector&amp;lt;int&amp;gt; &amp;amp;count) {
	int carry = 0;
	for(int digit = count.size() - 1; digit &amp;gt;= 0; digit--) {
		count[digit] = count[digit] + 1 + carry;
		carry = 0;
		
		if(digit == 0 &amp;amp;&amp;amp; count[digit] &amp;gt;= 10) {
			count[digit] = count[digit] % 10;
			count.insert(count.begin(), 1);
		} else if(count[digit] &amp;gt;= 10) {
			count[digit] = count[digit] % 10;
			carry = 1;
		} else {
			break;
		}
	}
}

void printCount(vector&amp;lt;int&amp;gt; &amp;amp;count) {
	for(auto num : count) {
		cout&amp;lt;&amp;lt;num;
	}
	cout&amp;lt;&amp;lt;&quot;\n&quot;;
}

int main(void) {
	int N; cin&amp;gt;&amp;gt;N;
	
	vector&amp;lt;int&amp;gt; count = {0};
	for(int i = 0; i &amp;lt; N; i++) {
		mul2(count);
		plus1(count);
	}
	printCount(count);
	
	if(N &amp;lt;= 20) {
		Hanoi(N, 1, 2, 3);
	}
	return 0;
}&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;/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;배울 수 있었던 점&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++에서 long long보다 큰 정수를 다루어야 할 때, 정수를 벡터로 표현하는 방법을 익힐 수 있었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;더 생각해 볼 점&lt;/b&gt;&lt;/h4&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;b&gt;1을 더하는 함수&lt;/b&gt;만 구현하였다. 이를 일반화하여, 원하는 숫자를 더하거나 곱하거나 빼거나 나누는 함수도 만들 수 있지 않을까?&lt;/li&gt;
&lt;li&gt;long long보다 큰 정수를 벡터가 아니라 문자열로 다루는 방법도 생각해 볼 수 있지 않을까?&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>알고리즘</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>오버플로우</category>
      <category>자료형</category>
      <category>재귀</category>
      <author>어썸min</author>
      <guid isPermaLink="true">https://awesomemin.tistory.com/18</guid>
      <comments>https://awesomemin.tistory.com/18#entry18comment</comments>
      <pubDate>Sat, 23 Mar 2024 14:41:42 +0900</pubDate>
    </item>
    <item>
      <title>2023년 회고 - 군생활 50%</title>
      <link>https://awesomemin.tistory.com/12</link>
      <description>&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;정신을 차려보니 2023년이 끝나있다.&lt;br&gt;&lt;br&gt;회고를 쓰려고 오랜만에 블로그에 들어와서 작년 회고를 읽어보려 했는데... 헉! 작년 회고를 안 썼더라. 그래서 작년 연말에 무엇을 하고 있었나 생각을 해봤는데 학원 강사 생활을 하면서 저녁에 강의만 스을쩍 하러 다녔고, 또 여러모로 힘든 시기를 겪고 있었기에 회고를 쓸 생각조차 못했던 것 같다. 그래서 2023년 회고에 앞서 2022년을 짧게 되돌아보겠다.&lt;br&gt;&lt;br&gt;자세히 말할 수는 없지만 &lt;b&gt;2022년&lt;/b&gt;은 내 인생에서 가장 힘든 한 해였다. 고등학교 2학년 때 힘든 일을 겪으면서 앞으로 내 삶에 이보다 힘든 1년은 없을 것이라고 확신했으나 인생이라는 것은 그리 호락호락한 것이 아니었다. 그러한 힘든 일이 내게 찾아온 탓에 &lt;a href=&quot;https://awesomemin.tistory.com/m/10&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;창대한 목표를 가지고 휴학을 했으나&lt;/span&gt;&lt;/a&gt; 정작 처음에 목표한 바는 아무것도 이루지 못했고, 내 성장을 위해 투자해야 할 소중할 시간들을 헛된 걱정과 불안에 휩싸여 낭비했다. 1년을 그냥 낭비해 버렸다는 자책감이 들었고 남들보다 1년 뒤처진다는 기분도 들었다. 그나마 9개월 정도 수학 강사 생활을 하면서 조금 모은 돈 정도가 위안이 됐다.&lt;br&gt;&lt;br&gt;어쨌든 2022년 12월에 학원 강사를 그만두고 2023년 3월 6일에 지원보급병으로 논산 육군훈련소에 입대했다. &lt;b&gt;입대하기 전 3개월&lt;/b&gt; &lt;b&gt;동안은&lt;/b&gt; 아무것도 안 하고 놀기만 했다. 돈을 아주 펑펑 쓰고 뇌도 도파민으로 물들였다. 지금 생각해 보면 그렇게까지 재밌지는 않았던 것 같다.&lt;br&gt;&lt;br&gt;&lt;b&gt;훈련병&lt;/b&gt; 생활은 특별한 것이 없었다. 학원일을 하면서 목이 좀 안 좋아졌었는데, 분대장 훈련병(반장 같은 개념)을 하면서 계속 소리를 질러야 해서 목이 그냥 많이 아팠다. 새벽에 완전군장 메고 행군하던 모습이 기억에 좀 남는 것 같다.&lt;br&gt;&lt;br&gt;훈련병 마치고 2주간 &lt;b&gt;후반기 교육&lt;/b&gt; 때는 그냥 뭐 너무 편했다. 훈련병 때부터 써왔던 사업 아이디어 노트가 계기가 돼서 같은 생활관에 창업에 관심 있는 동기랑 좀 친해지게 됐다. 교육도 열심히 들었더니 시험에서 1등을 해서 휴가도 3일 받았다.&lt;br&gt;&lt;br&gt;&lt;b&gt;자대&lt;/b&gt;는 어쩌다보니 다시 육군훈련소에 배치됐다. 다른 동기들은 육훈소 보급병 빡세다고 싫어했는데, 나는 유명한 부대여서 그냥 좋았다.&lt;br&gt;&lt;br&gt;자대에 와서 가장 먼저 내가 노력한 것은 &lt;b&gt;운동&lt;/b&gt;이다. 일주일에 3일 이상은 푸시업, 윗몸일으키기, 스쿼트, 달리기를 하고 있다. 주변에서 다들 운동을 하는 분위기인 것이 도움이 되는 것 같다.&lt;br&gt;&lt;br&gt;운동과 관련해서 가장 기억에 남는 성과는 &lt;b&gt;상병 진급 시험에 통과&lt;/b&gt;한 것이다. 사실 난 입대하기 전에 팔굽혀펴기를 10개도 못했다. 입대하고 나서는 조금씩 운동을 해서 20개 정도는 하게 되었는데, 상병 진급을 위해서는 한 번에 48개를 해야했다. 상병 진급을 한 달 반 남기고 발등에 불이 떨어진 나는 매일 점심시간, 체력단련시간, 개인정비시간을 다 투자해서 운동을 하기 시작했다. 그런데 개수가 느는 속도가 너무 더뎠다. 체력측정 일주일 전까지도 40개가 한계였다. 나는 해도 안 되는 건가 싶은 생각이 들었고 포기하고 싶었지만 놀랍게도 체력 측정 전 마지막 연습 때 합격선에 들어왔고 체력 측정 때 팔굽혀펴기, 윗몸일으키기, 달리기 모두 합격을 해서 상병에 진급할 수 있었다. 사실 다른 동기들을 보면 대부분 운동을 거의 안하거나 조금만 하고도 48개 정도는 가볍게 하는 것 같아보였다. 그걸 보면서 난 약하게 태어나서 안된다는 생각과 함께 절망감이 들었다. 그러나 포기하지 않고 끝까지 노력해서 목표를 이뤄낸 것에 스스로가 자랑스럽고 큰 성취감을 느껴 가장 기억에 남는 순간 중 하나인 것 같다. 또한 나는 살면서 무언가를 할 때 난 그것을 '잘하는 편'에 속하는 경우가 대부분이었기 때문에, 내가 잘 못하는 일에 대해 노력하고 경쟁하는 것이 생소했다. 그런 환경에서 1등은 아니더라도 내 목표를 이루었다는 것이 굉장히 소중한 경험이고, 내 시야를 넓혀주었다는 생각이 든다.&lt;br&gt;&lt;br&gt;입대하고 당연히 &lt;b&gt;학업&lt;/b&gt;도 손에서 놓지 않으려고 많은 노력을 했다. 나의 가장 큰 관심 분야인 창업과 컴퓨터 중, 처음에는 &lt;b&gt;창업&lt;/b&gt;에 대해 더 많은 관심을 두려고 했다. 그래서 훈련병 때부터 시간 날 때마다 창업 아이디어 노트도 써보고, 자대에 와서는 창업에 관련된 책도 읽었다. 그런데 책을 3권 정도 읽었을 때 쯤(6월 즈음), 창업을 직접 해볼 수도 없는 군대라는 환경에서 창업 그 자체에 대해 공부하는 것은 무의미한 것 같다는 생각을 했다. 당연히 창업을 하려면 창업에 대해 잘 알아야하지만 결국 그 근본은 고객에게 가치를 제공할 수 있는 서비스인 것이고 따라서 근본적으로 그러한 서비스를 '잘 만들 수 있는 기술력'을 가지는 것이 우선이라는 생각이 들었다. 그 이후로 공부의 방향을 창업에서 컴퓨터로 틀게 되었다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;2160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNkfGZ/btsCR97q8VL/NjmToFgDiyRdcnVW3y8yv1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNkfGZ/btsCR97q8VL/NjmToFgDiyRdcnVW3y8yv1/img.jpg&quot; data-alt=&quot;자대 와서 읽었던 창업 관련 도서들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNkfGZ/btsCR97q8VL/NjmToFgDiyRdcnVW3y8yv1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNkfGZ%2FbtsCR97q8VL%2FNjmToFgDiyRdcnVW3y8yv1%2Fimg.jpg&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;3840&quot; height=&quot;2160&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;2160&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;자대 와서 읽었던 창업 관련 도서들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;그렇게 &lt;b&gt;컴퓨터&lt;/b&gt; 공부의 방향성에 대해 고민하기 시작했다. 아무래도 컴퓨터를 접하기 힘든 군대라는 환경의 특성상 사회에서 하던 것처럼 강의를 들으며 웹개발을 공부하는 것은 제한사항이 있었다. 컴퓨터와 함께 일하는 행정병이라는 내 보직이 컴퓨터 공부를 하는 데 있어서 도움이 될까 기대를 해보았지만 인터넷 연결이 안되는 인트라넷PC의 특성상 보직의 도움을 받기는 힘들었다. 사실 인트라넷 컴퓨터에서 메모장으로 2048이나 스네이크 게임을 만들어보긴 했는데, 선임들의 장난감으로 전락해버렸다... 메모장에 코딩하는 극한의 노가다는 잠시 접어두기로 했다.&lt;br&gt;&lt;br&gt;그래서 방향을 틀어 책으로 할 수 있는 CS(Computer Science) 공부를 하기로 결정했다. 나는 사회에서 CS 지식 기반이 부족한 채 웹개발을 해왔기도 하고, 컴퓨터공학 비전공자라는 나의 약점을 보완할 수 있는 길이라는 생각이 들어 좋은 아이디어라고 생각했다. 자기개발비용으로 자료구조, 컴퓨터구조, 운영체제 책들을 사서 차례대로 공부했다. 동시에 컴퓨터 전반에 대한 교양서도 사서 읽었다.&lt;br&gt;&lt;br&gt;[&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;지금까지 공부한/읽은 책 목록]&lt;/span&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;li&gt;혼자 공부하는 컴퓨터구조/운영체제&lt;/li&gt;&lt;li&gt;Operating System Concepts (공룡책)&lt;/li&gt;&lt;li&gt;유닉스의 탄생&lt;/li&gt;&lt;li&gt;CODE&lt;/li&gt;&lt;li&gt;객체지향의 사실과 오해&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;그 중에 공룡책은 원서를 사서 읽었는데 한 50% 정도 공부를 하다가 너무 힘들고 효율이 떨어지는 것 같아서 임시 중단했다. 나중에 전역 후 또는 여유가 있을 때 코드 실습도 해보면서 다시 공부해보려고 한다. 아무튼 계속 책으로 공부를 하다보니 너무 컴퓨터 붙잡고 코딩이 하고 싶어서 2024년에는 사지방 가서 백준 문제풀이라도 꾸준히 해보려고 한다. 전역하고나면 군대에서 공부한 CS지식을 기반으로 새로운 프레임워크 등을 공부할 때 더 깊게 이해할 수 있기를 기대해본다.&lt;br&gt;&lt;br&gt;자기개발적인 부분 외의 군생활에는 별 문제 없이 잘 해나가고 있는 것 같다. 동기들과도 사이 좋게 지내고 또 간부님들(특히 행보관님)께도 좋게 보여지는 듯 싶다. 정말 웃긴 게, 나는 사회에서 하던 노력의 절반도 안 하는 것 같은데 주변에서 내가 일을 가장 열심히 하고 또 행보관님께 가장 인정받는 병사가 됐다.&lt;br&gt;&lt;br&gt;아 그리고 한 번 &lt;b&gt;응급실&lt;/b&gt;에 실려간 적이 있었다. 이런 저런 스트레스에 많이 힘들어하던 시기에 화가 많이 나는 일이 있었는데, 그 일이 머리 속에서 떠나지 않고 계속해서 그 일만 생각하게 되면서 점점 더 화가 많이 나고 호흡이 가빠지더니 결국 발작을 일으키면서 쓰러졌다. 나중에 알게된 바로는 그게 과호흡 증상이라고 하는데, 그 상태가 거의 20분정도 지속됐다. 전력질주를 한 것처럼 헐떡거리는 호흡을 멈출 수가 없고 숨이 엄청 차고 어지러운데 점점 손발이 꼬이는듯한 느낌이 들고 고통스러웠다. 그래서 당직사령 차를 타고 응급실에 갔는데 난감스럽게도 응급실에 도착해서는 증상이 완화돼서 별다른 조치 없이 복귀했다. 나는 이런 증상이 계속 반복되면서 공황장애 같은 질환으로 발전하면 어떻게 하나 걱정을 했는데 다행히 그러지는 않았다. 스트레스 관리의 중요성에 대해 깨달을 수 있는 사건이었다.&lt;br&gt;&lt;br&gt;아무튼 2023년을 한 줄로 요약하자면 당연히 &quot;군생활 50%&quot;인데, 입대 전 내 인생의 가장 큰 장애물로 작용하던 병역 문제를 해결하게 됐다는 점에서 아주 의미가 크다고 생각한다. 입대 전 조금의 방황의 시기에서 벗어나 입대 후에는 다시 새로운 시작을 할 수 있을 것이라는 기대감에 전역이 아주 기다려진다.&lt;br&gt;&lt;br&gt;핸드폰으로 긴 글을 쓰다보니 적고 싶은 말이 많은데 한 절반도 못 적은 것 같고, 내가 처음에 생각했던 글과도 많이 다른 글이 된 것 같다. 원래는 내 감상과 느낀 점 위주로 쓰고 싶었지만 어쩌다보니 사실 나열 위주의 보고서같은 글이 되어버렸달까. 아쉽지만 이 작은 핸드폰으로 이 글을 편집하고 다시 적을 용기가 없어 이렇게 마치려고 한다. 2024년 회고록은 내 방 책상에 앉아 컴퓨터로 작성하고 있을 생각을 하니 가슴이 두근두근하다. 내년에는 더 생산적이고 더 많이 성장할 수 있는 군생활이 됐으면 한다. 공부 꼭 많이 하자. 화이팅!&lt;/p&gt;</description>
      <category>생각</category>
      <category>회고</category>
      <author>어썸min</author>
      <guid isPermaLink="true">https://awesomemin.tistory.com/12</guid>
      <comments>https://awesomemin.tistory.com/12#entry12comment</comments>
      <pubDate>Wed, 3 Jan 2024 19:18:53 +0900</pubDate>
    </item>
    <item>
      <title>휴학을 하는 마음</title>
      <link>https://awesomemin.tistory.com/10</link>
      <description>&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;br&gt;2022년 3월 20일, 2학년이 시작된 지 1개월여 만에 중도휴학했다.&lt;br&gt;&lt;br&gt;&lt;b&gt;- 이유&lt;/b&gt;&lt;br&gt;1. 휴학 소식을 인스타그램에 올리고 많은 사람들이 그 이유를 물었다. 이러한 질문들에는 &quot;개발, 창업, 그리고 여러 프로젝트들에 전념하고 싶었다&quot;라고 일관적으로 답변했다.&lt;br&gt;&lt;br&gt;2. 휴학을 결심하게 된 동기는 3월 20일 학회 회식에서 받은 자극이었다. 그곳에서 VC로부터 투자를 받고 창업 중인 분, 구독자 수만 명의 유튜브 채널을 운영하는 분, 그리고 어떤 블록체인의 노드로 참여해 3달 동안 3-40억을 벌어 포르쉐를 타고 다닌다는 어떤 사람의 이야기를 들었다. 그 외에도 현직에서 프로페셔널하게 일하고 계시는 선배님들을 보고 정말 멋있다고 생각하기도 했고, 연세대학교 블록체인 학회에서 몇십억을 벌었다는 이야기 또한 굉장히 자극적이었다. 이렇게 짜릿하고 가슴 뛰게 하는 이야기들을 직접 보고 들으면서, 나도 어서 빨리 내가 진정 원하는 일을 하고, 무언가 성과를 내고 싶다는 강한 욕구가 들었다. 이에 휴학을 (다소 충동적으로) 결심하게 되었다.&lt;br&gt;&lt;br&gt;3. 어느샌가부터 나의 꿈과 목표는 너무 커져서, 일반적인 방법으로는 이룰 수 없는 것이 되어버렸다. 큰 꿈을 꾸면서 일반적인 사람들과 같은 방법으로 살아가는 것은 굉장히 모순적인 일이라는 생각이 들었다. 나처럼 유난히 특출나지 않은 사람이 큰 성공을 이루려면 적어도 평범하게 살아서는 안된다는 것은 분명하다. 그런데 그냥 학교 다니다 보니 내가 다른 사람들과 다른 게 뭔지 잘 모르겠었다. 그래서 평범한 내가 남들보다 앞서갈 수 있는 그런 돌파구(?)를 찾고자 휴학하게 되었다.&lt;br&gt;&lt;br&gt;4. 너무 바빴다. 창업 준비, 하이클럽 활동, 블록체인 학회, 그리고 과학생회와 21학점의 수업을 모두 소화해야 했다. 사실 &quot;적당히 적당히&quot;를 추구했다면 간신히 마칠 수는 있는 양이었는데, 군대 가는 친구들을 위로해 줄 시간조차 없을 것 같다는 생각에 현타가 왔다. 또한, 멋있는 사람들을 만나 같이 창업을 준비하게 되었는데, &quot;적당히 열심히&quot;해서는 얻어갈 수 있는 것이 많이 없을 것 같았다. (현재로서는) 팀의 유일한 개발자로서 해야 할 일이 많은데, 다른 일들이 너무 바빠서 내가 해야 할 일을 하지 못하는 등... 피해를 주기 싫었던 점도 휴학을 결심하는 이유의 하나가 되었다.&lt;br&gt;&lt;br&gt;5. 군대 때문에 학업이 단절되는 것이 싫었다. 나는 (계열제 특성상) 1학년 때 교양 수업만 듣다가 2학년 때부터 처음 전공 수업을 듣기 시작했는데, 전공 수업을 1년 듣고 군대에 다녀와서 3학년이 되면 2학년 때 배운 전공 수업 내용들을 까먹는 것이 싫었다. 1년 휴학 후 군대를 다녀와서 3년간 전공을 쭉 공부하고 싶은 것도 휴학 결심의 이유 중 하나였다.&lt;br&gt;&lt;br&gt;&lt;b&gt;- 무엇을 할 것인가?&lt;/b&gt;&lt;br&gt;1. 프로그래밍 공부 및 서비스 개발 : &lt;a href=&quot;https://awesomemin.tistory.com/9&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;ClickBattle&lt;/span&gt;&lt;/a&gt;을 개발하고 운영하면서 내가 만든 서비스를 사람들이 사용해 주는 것에 굉장한 희열을 느꼈다. 그리고 내가 만든 서비스가 (물론 몇 천 원밖에 안되지만) '실제로 돈이 된다'라는 것이 굉장한 충격이었다. 그런데 클릭배틀을 운영하면서 실력 부족으로 수많은 버그들과 불친절한 UI 등을 개선하지 못했고, 자동 클릭 매크로의 존재를 알면서도 방치할 수밖에 없었다. 휴학 중에는 열심히 공부해서 이러한 문제점들을 해결할 방법을 찾고, ClickBattle의 사업화도 진행해보려 한다. 또한, ClickBattle 이외의 다른 서비스들에 대한 아이디어도 아주 많이 가지고 있는데 이들을 하나씩 구현하고 실험해 볼 것이다. 궁극적으로는 내가 개발한 서비스를 통해 매일 점심에 서브웨이를 사 먹을 수 있을 정도의 수익을 얻는 것이 목표이다. (요즘 점심으로 매일 서브웨이만 먹은 지 2주 정도 됐는데 안 질린다)&lt;br&gt;&lt;br&gt;2. DEX 차익거래 공부 : (떨어졌지만) 인턴 지원했던 기업의 개발팀장님과 잠깐의 티타임을 가질 수 있었다. 그 회사는 Klaytn 네트워크에서 DEX를 운영하는 곳이었는데, DEX 간의 차익거래를 통한 수익도 매출의 일부라고 했다. 나는 차익거래는 기관의 전유물이라는 인식을 가지고 있었는데, DEX에서는 블록의 생성시간이 있고, 아직 시장에 충분한 arbitrager(차익거래자)가 없어서 봇을 쓰지 않고 개인이 손으로 거래를 해도 수익이 날 수 있는 수준이라는 식의 말씀을 해주셨다. (내가 잘 알아들은 건진 모르겠다 ㅋㅋ...) 이 말을 듣고 언젠가 꼭 관련 공부를 해보고 싶다는 생각이 들었는데 휴학 중에는 이와 관련된 공부를 해보고 싶다.&lt;br&gt;&lt;br&gt;3. 창업 준비 : 사업을 시작해보고 싶다.&lt;br&gt;&lt;br&gt;4. 독서와 영어공부, 그리고 여행.&lt;br&gt; &lt;br&gt;&lt;b&gt;이왕 열심히 공부하자고 휴학했으니 정말 뭐라도 열심히 해서 꼭 성과를 내보자 아자아자~~&lt;/b&gt;&lt;/p&gt;</description>
      <category>생각</category>
      <author>어썸min</author>
      <guid isPermaLink="true">https://awesomemin.tistory.com/10</guid>
      <comments>https://awesomemin.tistory.com/10#entry10comment</comments>
      <pubDate>Sun, 3 Apr 2022 22:26:02 +0900</pubDate>
    </item>
    <item>
      <title>[프로젝트 회고] ClickBattle</title>
      <link>https://awesomemin.tistory.com/9</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;대학별 클릭 노가다 경쟁 게임&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;screen.jpg&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G9taZ/btrr6BeC62M/Rg6epoDYumA5TfpcR3b6Bk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G9taZ/btrr6BeC62M/Rg6epoDYumA5TfpcR3b6Bk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G9taZ/btrr6BeC62M/Rg6epoDYumA5TfpcR3b6Bk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG9taZ%2Fbtrr6BeC62M%2FRg6epoDYumA5TfpcR3b6Bk%2Fimg.jpg&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;600&quot; height=&quot;519&quot; data-filename=&quot;screen.jpg&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;프로젝트 깃허브&quot; href=&quot;https://github.com/awesomemin/clickBattle&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;프로젝트 깃허브 https://github.com/awesomemin/clickBattle&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;&lt;a title=&quot;프로젝트 페이지&quot; href=&quot;http://clickbattle.site&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;프로젝트 페이지&lt;/a&gt;&lt;/s&gt; &lt;s&gt;&lt;a href=&quot;http://clickbattle.site&quot;&gt;http://clickbattle.site&lt;/a&gt;&amp;nbsp;&lt;/s&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;/b&gt; : 2022.01.09. ~ 2022.01.30&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;주요 기능&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 대학교명과 이름을 입력하여 로그인 (쿠키를 이용해서 로그인 유지)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 파란색 '+1' 버튼을 클릭하여 점수 획득&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;성과&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초 배포 후 3시간&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 클릭 약 650,000회&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 방문자 수 약 800명&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;최초 배포 후 24시간&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 클릭 약 3,200,000회&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 방문자 수 약 3,300명&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;최초 배포 후 72시간&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 클릭 약 7,400,000회&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 방문자 수 약 4,000명&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;사용스택&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;[Front-End]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;- Javascript&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Axios : HTTP request 전송&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Bootstrap : UI 디자인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- nunjucks : HTML 동적 템플릿&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;[Back-End]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;- Node.js&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- MySQL : 데이터베이스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Express.js : 서버 프레임워크&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- dotenv : DB 비밀번호 관리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- helmet : Express 보안 미들웨어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- hpp : HTTP Parameter Pollution 방어 미들웨어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- winston : 로그 기록&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- morgan : 로그 기록&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- sequelize : ORM, Javascript로 MySQL&amp;nbsp; DB와 통신할 수 있게 해줌&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;[Deploy]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;- AWS Lightsail : 클라우드 가상 서버&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Gabia : 도메인 구매, 네임서버&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- pm2 : Process Manager, 서버가 종료되면 자동으로 재시작해줌&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;TroubleShooting&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;최초 배포 후 약 2시간 정도 지나서 트래픽이 늘면서 페이지가 먹통이 되는 문제가 발생했다. 에러 로그도 없고 서버가 종료되지도 않은 것으로 보아 서버 에러는 아닌 것 같았는데, 정확한 원인은 알 수가 없었다. 서비스 사용자들이 이탈할까봐 마음이 급해서 일단 Lightsail instance를 재시작하는 것으로 해결했다. 그러나 같은 문제가 30~60분 간격으로 약 4회정도 반복됐다. 그러다 Lightsail 모니터링 페이지에서 서버 CPU 부족이 원인임을 발견했다. (아래 사진 참고)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;cut.png&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;773&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTDFYt/btrr3jMtTpK/34jyaA4vRRXkk0XM5716d1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTDFYt/btrr3jMtTpK/34jyaA4vRRXkk0XM5716d1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTDFYt/btrr3jMtTpK/34jyaA4vRRXkk0XM5716d1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTDFYt%2Fbtrr3jMtTpK%2F34jyaA4vRRXkk0XM5716d1%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;400&quot; height=&quot;485&quot; data-filename=&quot;cut.png&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;773&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;원인 파악 후, 서버 CPU 사용량을 낮추기 위한 방법을 고민했다. &lt;b&gt;먼저, 기존 1.0초로 설정되어있던 실시간 랭킹 업데이트 주기를 1.5초로 늘렸다.&lt;/b&gt; (약 50% 성능 향상이 있을 것으로 기대했음) 그러나 CPU 사용량이 전혀 낮아지지 않았다. 생각해보니 서버에 부담을 주는 것은 1초에 단 한 번 수행되는 랭킹 업데이트 request가 아니라 사용자들이 클릭할 때마다 실행되는 점수 증가 request였다. 랭킹 업데이트 request는 setInterval 함수로 호출되기 때문에 호출 주기를 마음대로 조절할 수 있지만 점수 증가 request는 사용자들이 버튼을 누를 때마다 호출되기 때문에 어떻게 해결해야하나 고민이 되었다. (광클시 평균적으로 초당 10번 이상의 클릭이 발생했다. 즉, 사용자 1명당 초당 10개의 request가 발생했다.) &lt;b&gt;이를 해결하기 위해 front-end에서 사용자가 1초간 누른 횟수를 임시로 기록해두었다가, 매 1초마다 서버로 request를 전송하도록 했다.&lt;/b&gt; 이렇게 하면 사용자의 버튼 클릭 횟수와 관계없이 서버에 전송되는 request를 초당 1개로 제한할 수 있었다. (아래 코드 참고)&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;pre id=&quot;code_1643773611999&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// main.js (front-end)
btn.addEventListener('click', async () =&amp;gt; {
  const res = await axios.post('/main');
  indivScore.innerText = res.data.user[0].point + 1;
  univScore.innerText = res.data.univ[0].point + 1;
});

// app.js (back-end)
app.post('/main', async (req, res, next) =&amp;gt; {
  try {
    const user = await User.findAll({
      where: {
        id: req.cookies.id,
      }
    });
    const univ = await Univ.findAll({
      where: {
        name: user[0].dataValues.univ,
      }
    });
    await User.update({ point: user[0].dataValues.point + 1 }, {
      where: {
        id: user[0].dataValues.id,
      }
    });
    await Univ.update({ point: univ[0].dataValues.point + 1 }, {
      where: {
        id: univ[0].dataValues.id,
      }
    });
    const data = {
      user: user,
      univ: univ,
    };
    res.json(data);
  } catch (err) {
    next(err);
  }
})&lt;/code&gt;&lt;/pre&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;- 수정 후&lt;/p&gt;
&lt;pre id=&quot;code_1643774198094&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// main.js (front-end)
let clicksInSec = 0;

btn.addEventListener('click', () =&amp;gt; {
  clicksInSec++;
  indivScore.innerText = parseInt(indivScore.innerText) + 1;
})

setInterval( async () =&amp;gt; {
  const data = {
    point: clicksInSec
  }
  clicksInSec = 0;
  const res = await axios.post('/main', data);
  univScore.innerText = res.data.univ[0].point;
}, 1500);

// app.js (back-end)
app.post('/main', async (req, res, next) =&amp;gt; {
  try {
    const user = await User.findAll({
      where: {
        id: req.cookies.id,
      }
    });
    const univ = await Univ.findAll({
      where: {
        name: user[0].dataValues.univ,
      }
    });
    await User.update({ point: user[0].dataValues.point + req.body.point }, {
      where: {
        id: user[0].dataValues.id,
      }
    });
    await Univ.update({ point: univ[0].dataValues.point + req.body.point }, {
      where: {
        id: univ[0].dataValues.id,
      }
    });
    const data = {
      user: user,
      univ: univ,
    };
    res.json(data);
  } catch (err) {
    next(err);
  }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로, &lt;b&gt;서버의 CPU 사용률을 70% 이상에서 20% 언저리까지 끌어내릴 수 있었다.&amp;nbsp;&lt;/b&gt;아래 사진을 보면 CPU 사용량이 줄어든 것을 볼 수 있고, 특히 아래 그래프에서 잔여 CPU 버스트 용량이 줄어드는 속도가 2배 이상 느려진 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;802&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/edz82k/btrsc1MbhhB/uQy8uuBb1RQ4vvnYPUgLm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/edz82k/btrsc1MbhhB/uQy8uuBb1RQ4vvnYPUgLm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/edz82k/btrsc1MbhhB/uQy8uuBb1RQ4vvnYPUgLm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fedz82k%2Fbtrsc1MbhhB%2FuQy8uuBb1RQ4vvnYPUgLm1%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;400&quot; height=&quot;485&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;802&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;서비스에 트래픽이 몰리고 사용자가 늘면서 매크로를 사용하는 악성 사용자도 발생했다. 특히, 이전 문단에서 CPU 사용량을 줄이기 위해 점수 증가 request를 1초에 한 번씩 전송하도록 변경하는 과정에서 심각한 취약점이 발생했다. (예리한 독자라면 코드를 읽으면서 이미 눈치를 챘을지도 모른다.) 원래는 request 한 번에 점수도 1점씩 오르는 방식이었는데, 변경 이후에는 request body에 포함된 point의 값만큼 점수를 올리도록 설계하였다. (point = 사용자가 1초간 버튼을 누른 횟수) &lt;b&gt;그런데, point 값에 제한이 없어서 front단에서 point 값을 임의로 변경하여 request를 보내면 조작된 point 값만큼 점수가 증가하는 문제가 있었다.&lt;/b&gt; 처음엔 이 문제를 모르다가, 점수가 1초에 300씩 꾸준히 증가하는 사용자를 발견하고 로그를 읽어보다가 문제를 깨달았다. 문제를 파악한 이후에 즉시 다음 코드를 추가하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1643776131970&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if(req.body.point &amp;gt;= 25) {
  req.body.point = 25;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이를 통해 초당 25번이 넘는 클릭에 대해서는 단 25번만 클릭한 것으로 간주되도록 처리했다.&amp;nbsp;&lt;/b&gt;이렇게 문제가 해결된 것처럼 보였다. 그런데 훨씬 더 심각한 문제가 곧 발생했다. 클릭 횟수 백만 번이 넘던 1등 대학의 점수가 0점으로 초기화된 것이다. &lt;b&gt;point의 값이 음수가 되는 것에 대한 제한이 없었던 게 문제였다.&amp;nbsp;&lt;/b&gt;어떤 악성 사용자가 point에 거대한 음수를 넣고 request를 전송한 것으로 보였다. 다행히 문제를 빨리 인식하고 point 값이 음수가 될 수 없도록 제한하고, 대학 점수도 복구시켰다. point 값이 당연히 양수가 될 의도로 코딩했지만, &lt;b&gt;프로그램은 내 의도에 따라 움직이는 게 아니라 작성된 코드에 따라 움직인다는 것을 뼈저리게 깨달았다.&lt;/b&gt; (특히 타입의 구분이 없는 Javascript라 더욱 그런 것 같다. 얼른 Typescript를 공부해야겠다.)&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;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 생각보다 많은 트래픽이 몰려서 놀랐다. 나같은 초보 개발자가 이런 트래픽을 경험해볼 기회가 흔치 않은데, 정말 귀한 경험이었고 많이 배웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 에브리타임 홍보게시판과 인스타그램 스토리에 올린 것 빼고는 마케팅에 힘을 쏟지 않았는데도 클릭배틀이 정말 많이 퍼져나간 것 같다. 이래서 바이럴 바이럴 하나보다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 개발 중에 엄마가 &quot;그렇게 단순한 게임을 누가 하니?&quot;라며 뭐라했는데, 많이들 이용해주어서 기뻤다. 역시 우리나라 사람들은 뭐든 지는 것을 싫어한다. 경쟁심을 자극하는 서비스는 성공할 확률이 높은 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. Google Adsense 광고를 신청해두었는데, 트래픽이 다 죽을 때까지 광고가 달리지 않았다. 만약 광고가 달렸다면 적어도 도메인 구매 비용정도는 메꾸었을텐데 참 아쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 사람들이 정말 글을 안 읽는다. 개인 랭킹 화면에서 아무 이름이나 클릭하면 학교 랭킹으로 돌아간다고 써두었는데 그걸 모르는 사람들이 많더라. &lt;b&gt;앞으로 UI를 만들 때는 과할 정도로 친절해야겠다고 느꼈다.&lt;/b&gt;&lt;/p&gt;</description>
      <category>프로젝트</category>
      <category>프로젝트</category>
      <category>프로젝트회고</category>
      <category>회고</category>
      <author>어썸min</author>
      <guid isPermaLink="true">https://awesomemin.tistory.com/9</guid>
      <comments>https://awesomemin.tistory.com/9#entry9comment</comments>
      <pubDate>Wed, 2 Feb 2022 14:05:45 +0900</pubDate>
    </item>
    <item>
      <title>2021년 회고 - 성균관대학교에서의 첫 1년</title>
      <link>https://awesomemin.tistory.com/8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;1. 입시 결과는 사실 좀 부끄럽다. 고등학교 3년 간 공대를 목표로 공부했는데, 수시 원서를 쓸 무렵 정형외과 전문의 월급이 1,000만원은 기본으로 넘는다거나, 한의사가 되면 20대에 월급 700만원은 기본이라거나 하는 소리를 듣고 급 전문직 뽕을 맞았다. (망할 오르비..) 그런데 의대나 치대에 지원하기엔 성적이 조금 모자라서 한의대 수시에 원서 6장 중 4장을 넣는 천하의 기행을 저지르고 말았다. (그런데 사실 한의대 가기에도 성적이 조금 모자라긴 했다.) 당연하게도 한의대는 네군데 모두 떨어지고 성균관대 공대에 오게 되었다. 그리고 작년에 한의대에 원서를 낭비하느라 SKY 대학에 지원조차 못해본 게 아쉬워서 올해 고려대 산업공학과에 지원해봤는데 탈락했다. 처음엔 입시 결과에 조금 아쉽기도 했는데, 대학에서 공부하면서 정말 똑똑하고 유능한 학우분들이 많은 것을 보고 지금은 자부심을 가지고 다니게 되었다.&lt;br&gt;&amp;nbsp;&lt;br&gt;2. 대학에서 가장 놀랐던 점은 사람들이 영어로 된 텍스트를 다루는 태도에 관한 것이었다. 고등학교 때는 영어 시간 외에는 영문을 읽을 일이 없었는데, 대학교에 와서는 많은 수업이 영어로 진행되는 것이 놀라웠다. 수업 뿐만 아니라 내가 활동했던 블록체인 학회에서도 영어로 된 자료로 공부를 했고, 교수님께서 읽어보라고 던져주시는 자료들도 영어 자료가 많았다. 나는 이런 환경에 적잖이 충격을 받았는데, 다른 사람들은 전혀 그런 것 같지 않아서 또 한 번 충격을 받았다. 그래도 1년동안 학교에 다니면서 나름대로 적응을 했다. 그래서 지금은 입학했을 때보다 영어에 대한 거부감도 줄었고 영여로 된 글에서 정보를 뽑아내는 능력도 많이 향상된 것 같다. (그리고 시험기간에 카페에서 영어로 된 두꺼운 미적분 책을 펴고 공부하고 있으면 뭔가 엄청난 공부를 하고 있는 것 처럼 느껴져서 괜한 지적 허영심을 채우기에도 좋다... 그래봤자 학부 1학년 수준인데 ㅋㅋ)&lt;br&gt;&amp;nbsp;&lt;br&gt;3. 대학에서의 인간관계는 쉽지 않은 것 같다. 고등학교 때는 좋든 싫든 하루의 거의 절반을 붙어있어야 했기 때문에 같은 반 사람들 중 친한 친구가 생길 수밖에 없었는데, 대학교는 확실히 다르더라. 내가 먼저 노력하지 않으면 알아서 친구가 생기는 시절은 지난 것 같다. 그런데 내가 먼저 적극적으로 나서기엔 용기도 부족하고 또 새로운 사람과 가까워지는 그 과정이 너무 피로하다. 그래서 그냥 가만히 있자니 진짜 '친구'라기보단 '그럭저럭 아는 사람'들이 많이 생겨나는 것 같다. '그럭저럭 아는 사람'들 중엔 더 친해지고 싶은 사람들도 많이 있는데도 더 친해지기 위한 노력을 전혀 하지 않는 내가 가끔은 이해되지 않을 때도 있다. 나는 어릴 때부터 항상 스스로가 외향적인 사람이라고 믿어왔는데, 요즘 드는 생각은 어쩌면 내가 외향적인 사람의 가면을 쓴 내향적 사람일지도 모르겠다는 것이다. 나는 '얕고 넓게'보다는 '좁고 깊게'가 잘 어울리는 사람인 것 같다. 그리고 특히 코로나때문에 성균 사이버대학에 다니고 있는 터라 더 사람 만나기가 힘든 것 같기도 하다. 그래도 대학교에 자주는 아니지만 종종 연락하고, 가끔은 만나기도 하는 고마운 사람들이 좀 있다. 그 사람들에게 참 고맙다. 그리고 성인이 되고 각자의 길을 걷고 있지만 그래도 항상 힘이 되어주는 오래된 친구들에게도 고맙다. 그 외 많은 사람들에게도 참 고맙다.&lt;br&gt;&amp;nbsp;&lt;br&gt;4. 언제부턴가 '돈'이 내게 있어 가장 큰 가치가 되었다. 중학생 때까지만 해도 나는 천문학자가 되기를 꿈꾸며 &quot;돈은 못 벌어도 하고싶은 일을 하며 살래&quot;라고 말하던 소년이었다. 많은 돈을 버는 것이 인생의 목표가 된 것은 대략 고등학교 2학년 때 즈음이었던 것 같다. 특별한 계기가 있었던 것은 아니지만, 어느 순간부터 그렇게 됐다. 자본주의 사회를 하나의 게임이라고 생각하면 돈은 일종의 점수(score)라고 생각한다. 우리는 게임을 할 때 높은 점수를 얻어서 랭킹에 들고자 많은 시간을 들이면서 재미를 얻는다. 인생도 그렇게 살면 더 보람찰 것 같다고 생각했다. &quot;많은 돈을 벌어서 무엇을 사고싶다, 어떠한 삶을 살고싶다&quot;라는 생각보다는 돈 자체를 많이 벌고싶다는 욕구가 강하다. 돈을 수단으로서 생각하기보다는 많은 돈 그 자체를 목적으로 가지게 된 것 같다. 그런데 내가 벌고자 하는 돈의 양은 남들의 상상을 초월할 정도로 많기 때문에, 평범한 일을 해서는 이룰 수 없다고 생각했다. 그래서 내 인생의 목표를 이룰 수 있는 유일한 방법은 창업이라고 생각한다. 그래서 창업을 할 예정이다. 매일 많은 생각을 하고 또 괜찮은 아이디어들을 가지고 있지만, 아직은 조금 어렵게 느껴진다. 당장 무엇을 시작하고자 하는 의지는 있지만 군입대를 1년 남짓 앞둔 사람으로서 &quot;지금 시작하는 것이 맞는가?&quot; 하는 의문이 자꾸 든다. 생각과 말만으로는 아무 것도 변하지 않는다. 2022년에는 무엇이라도 일단 시작해보자.&lt;br&gt;&amp;nbsp;&lt;br&gt;5. 대학에 와서 많은 노력을 했고, 실제로 많은 성장을 했다. 그러나 1년이라는 긴 시간의 길이를 따져봤을 때, 이번 연도 나의 실질적 성장 속도는 너무 느리다. 간단한 프론트엔드 개발 외에는 할 줄 아는 것이 거의 없고 (이마저도 불완전하다) 백엔드는 이제 막 공부를 시작했다. 규모 있는 프로젝트를 처음부터 끝까지 진행한 경험도 없고, 이렇다할 창업 경험을 쌓지도 못했다. 그나마 위안이 되는 점들을 따져보자면 두 학기동안 42학점을 듣고도 썩 괜찮은 성적을 받았다는 점, 블록체인 확회 활동을 통해 (내가 도움이 되어주진 못했지만) 팀 프로젝트의 전반적인 프로세스를 경험해봤다는 점, 외국인 교환학생과 단 둘이 식사를 하는 특별한 경험을 했다는 점, 그리고 이런 미약한 활동이나마 인정을 받아 장학금 수혜자로 선정되었다는 점이 있겠다. 그러나 내가 창업을 하고 개발을 하며 결과적으로 성공하는 데 있어서 실질적으로 필요한 역량을 폭발적으로 기를 수 있었는지는 의문이다. 지금 생각해보면 덜 중요한 일들에 밀려 정말 필요한 능력을 기르지 못하고 있었던 것 같다. 그나마 처음 성인이 되어서 대학 생활에 적응하느라 공부할 시간이 없었다는 핑계를 대본다. 그리고 제발 유튜브 그만 보자. 이제부터는 내게 정말 필요한 역량을 찾아 폭발적으로 성장할 수 있도록 노력해보겠다.&lt;br&gt;&amp;nbsp;&lt;br&gt;6. 악한 사람은 없고, 선한 사람도 없다. 누구나 악한 면과 선한 면을 함께 가지고 있다. 그래서 같은 사람에 대한 평가도 다양하게 나뉠 수 있다. 이번 1년간 나는 주변 사람들에게 어떻게 인식되었을까? 내 생각엔 나를 '선한 사람'보다는 '악한 사람'에 가깝게 인식한 사람이 더 많을 것이다. (아닐 수도 있다) 그리고 나 자신에 대한 나의 평가도 많이 달라진 한 해였다. 평생을 '선한 사람에 매우 가까운 사람'으로 살아온 나였기에 (마찬가지로 아닐 수도 있다) 조금은 혼란스럽기도 하다. 1년 뒤의 나는 스스로를 어떻게 생각할지 궁금하다.&lt;br&gt;&amp;nbsp;&lt;br&gt;7. 이 재미없는 글을 끝까지 읽어준 사람이 있다면 정말 감사하다.&lt;/p&gt;</description>
      <category>생각</category>
      <category>회고</category>
      <author>어썸min</author>
      <guid isPermaLink="true">https://awesomemin.tistory.com/8</guid>
      <comments>https://awesomemin.tistory.com/8#entry8comment</comments>
      <pubDate>Tue, 21 Dec 2021 01:50:25 +0900</pubDate>
    </item>
  </channel>
</rss>