{
    "componentChunkName": "component---src-templates-blog-template-js",
    "path": "/knuhr-design/",
    "result": {"data":{"cur":{"id":"6e96b2f2-346c-50bb-be05-ff0aeda2b167","html":"<p>학교 멘토링 프로그램으로 카카오 판교 아지트를 방문하고 자극을 받은 뒤, 2학기가 시작하기 직전인 8월 중순 무렵부터 새로운 프로젝트를 시작했다.</p>\n<p>주제로 삼은 건 경북대 학생들을 위한 프로젝트/스터디 구인구직 사이트.<br>\n이미 우리 과에서는 7월 쯤부터 노션을 통해 학부생들을 위해 서비스를 제공하고 있었지만, 노션으로 사용하기에는 불편함을 느껴 웹으로 만들어보고 싶다는 욕심이 생겼다.<br>\n그렇게 새로운 프로젝트를 시작했고, 지금까지 진행한 프로젝트 진행 상황과 앞으로 있을 프로젝트 진행 상황에 대해서 포스팅 해볼 예정이다.</p>\n<h2 id=\"-요구사항-정의-및-기능-설계\" style=\"position:relative;\"><a href=\"#-%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD-%EC%A0%95%EC%9D%98-%EB%B0%8F-%EA%B8%B0%EB%8A%A5-%EC%84%A4%EA%B3%84\" aria-label=\" 요구사항 정의 및 기능 설계 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🧾 요구사항 정의 및 기능 설계</h2>\n<p>우선 사용자 요구사항 정의서를 작성하면서 프로젝트에 필요한 기능들을 생각했다.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 720px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 40.55555555555556%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABRklEQVQoz02Ria6sIBBE+f9ffOOK6+CGgAo61k23Sx4J6QbSJ1WFMGaG1hr7vmNdVwxDjxACgt8w9R38uuA8T970rvUE5yx+vx/v4zh47yFwFfOsGUBrWRZYa7nfvcfYVjBjz2cCtm2DJI4QRxGi6IM4jvD5/ENRSGRZiqapIawx8N7zEClw1vLwHjzmocNi5hdIAwRLkxhpmjBc5jmrJmfEEgR5FFKlMw0fe4DTIzZnX8tdpyDzDHmesaqqLBlI6pIkRtd1EJTftm23ZYd1XS6439iynQacuJZSCnVdsVKqD/T7bVEWBf+FmMbxhRhj4Jy7gNuKvpSYe4XzRjZNAylzzrKQkvu6qtDUNauke0Ef8VimLJ+eLNvH8p2hUl9WRTDKkACUnZ4mFkKzgi4ISot+/H+F3a0QL1DdCltWlGfZVe9MKf8/0sFjLeCOInwAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"KNUHR-RFP.png\"\n        title=\"KNUHR-RFP.png\"\n        src=\"/static/32108dd7f6091fd716816d269e3c3ba9/37523/KNUHR-RFP.png\"\n        srcset=\"/static/32108dd7f6091fd716816d269e3c3ba9/e9ff0/KNUHR-RFP.png 180w,\n/static/32108dd7f6091fd716816d269e3c3ba9/f21e7/KNUHR-RFP.png 360w,\n/static/32108dd7f6091fd716816d269e3c3ba9/37523/KNUHR-RFP.png 720w,\n/static/32108dd7f6091fd716816d269e3c3ba9/302a4/KNUHR-RFP.png 1080w,\n/static/32108dd7f6091fd716816d269e3c3ba9/07a9c/KNUHR-RFP.png 1440w,\n/static/32108dd7f6091fd716816d269e3c3ba9/21cdd/KNUHR-RFP.png 1680w\"\n        sizes=\"(max-width: 720px) 100vw, 720px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n    </span></p>\n<center>[사용자 요구사항 정의서]</center>\n<br/>\n<p><strong>회원가입/로그인</strong> 부분은 전체 서비스가 경북대 학생들을 위한 서비스이기 때문에 <code class=\"language-text\">@knu.ac.kr</code> 메일 인증을 거쳐서 <strong>회원가입</strong>을 하고 <strong>로그인</strong> 할 수 있도록 기능을 설계했다.<br>\n그 다음에는 서비스에서 가장 중요하고 없어서는 안되는 기능인 <strong>모임 CRUD</strong>와 <strong>참가 기능</strong>을 넣었다.<br>\n모임에는 <strong>카테고리</strong>와 <strong>태그</strong>를 넣어서 검색 시에 필터링 할 수 있도록 구현할 예정이다.</p>\n<p>다음으로는 <strong>채팅 기능</strong>에 대해 설계했다.<br>\n<strong>채팅 기능</strong>도 이 프로젝트를 시작하게 된 큰 이유 중 하나다.<br>\n기존 노션을 이용한 구인구직 시스템에는 채팅 기능이 없어서 모임장은 자신의 연락처를 남기고 참여를 원하는 사람이 직접 연락을 하는 방식이었는데, 이는 개인정보 유출 문제도 생길 수 있을 뿐더러 연락을 위해 플랫폼을 옮기는 것부터가 사용자 친화적이지 못하다고 생각해서 <strong>채팅 기능</strong>을 꼭 넣어야겠다고 생각했다.</p>\n<p>프로젝트는 크게 <strong>프론트엔드</strong>는 <code class=\"language-text\">React</code>, <strong>백엔드</strong>는 <code class=\"language-text\">Spring Boot</code>를 사용하여 개발하기로 했다.<br>\n특히 이번에는 개인적으로 저번 프로젝트에서 많이 다뤄보지 못한 Spring을 다뤄볼 수 있는 기회라고 생각하고 프로젝트에 임했다.</p>\n<p>🛠 기술 스택</p>\n<blockquote>\n<p><strong>프론트엔드</strong></p>\n<ul>\n<li>React</li>\n<li>Tailwind CSS</li>\n<li>React Query</li>\n<li>Vite, Vitest</li>\n</ul>\n</blockquote>\n<blockquote>\n<p><strong>백엔드</strong></p>\n<ul>\n<li>Java</li>\n<li>Spring Boot</li>\n<li>MySQL</li>\n<li>AWS EC2, RDS</li>\n</ul>\n</blockquote>\n<hr>\n<h2 id=\"-usecase-diagram\" style=\"position:relative;\"><a href=\"#-usecase-diagram\" aria-label=\" usecase diagram permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🙆 Usecase Diagram</h2>\n<p><strong>Usecase Diagram</strong>을 그리기 전에 위에서 정의한 요구사항 정의서를 바탕으로 먼저 전체 흐름을 적어두고, <strong>Actor</strong>와 <strong>Usecase</strong>를 나눈 다음 다이어그램을 그렸다.</p>\n<p><strong>Actor</strong>는 크게 모임을 만드는 <code class=\"language-text\">모임장</code>과 원하는 모임에 신청하는 <code class=\"language-text\">신청자</code>, 그리고 처음 서비스를 이용하는 <code class=\"language-text\">신규 유저</code>로 나눴다.<br>\n<strong>Usecase</strong>는 서비스에 필요한 기능들을 하나씩 나누었고, 이 기능들 간의 관계를 생각해서 다이어그램을 그렸다.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 720px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 80.55555555555556%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAACV0lEQVQ4y4WUv67aMBTGT2+v2qEDUqW+ARvrhduh6tKhUwemPg0P0kvvu0RCMLPBAAN/EhIS8t9JwHZcfVYcpbetasnYTpyfv++cY0gpda+UojAMp9frVVVVxauqwqiwRr/dbqr7zMyrqqoxFkUhN5vNaLfbEc1mMwKwLMupUkrFcczP57P+gHOuxzAMdfd9X4+dVuufupaHw2Hkui7Rfr8fARjH8Q98nGUZz/NcqyqKQqVpqsqyVIyxtjeQFiiEkOA4jkNkWdYXInobBMFTs4mr/zQcjIPyPK+llEpKqYGn04nQXnctCyE4lOEjHPCyo+H95XJBeGohhAEONXA4HH4AsCiKJ2xmjPEgCLBZx7ANVgcIVYhl3TyQUgrbth/Boclk8qYBTrGxKAqO+GGO/Ygf1p24abuO4yDj2jLnvNput1+Xy+U37RnANE112ZgYNuWglSZJ8ps62IXCJElqHFaWpbAs6zMR3QP2DkDP854BLMuSe56nSyTLMg1Dx7tuLHEYY0zHEMD5fP6JiO5otVp9HwwGPdd1p1Dj+z6HnSiKtErAoAhw06DKtm0VBEGN50IIsV6vPxLRKxqPx++hMMuyn42lP8oGANSfqUfYxbyTFHk8HoeLxYKo3+/3APR9/7lJBDfxQpZhFcpd19WJwBz7ujfF1GEcx0QPDw89UzaISxRFHDZxW7DuWoUq2Efcmqy3QFw9XYeAmbLBxiRJNBDWoMT8GZhk4CCs/wkUQuibkuf5tNnUWjaqAId9FDziZxLWtdxVeAcgY6wFvrwZphnFpoT+BvwFGmx6L7yJC6EAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"KNUHR-UCD.png\"\n        title=\"KNUHR-UCD.png\"\n        src=\"/static/e4dc33ec40ab4c8d7737c173ea116ad1/37523/KNUHR-UCD.png\"\n        srcset=\"/static/e4dc33ec40ab4c8d7737c173ea116ad1/e9ff0/KNUHR-UCD.png 180w,\n/static/e4dc33ec40ab4c8d7737c173ea116ad1/f21e7/KNUHR-UCD.png 360w,\n/static/e4dc33ec40ab4c8d7737c173ea116ad1/37523/KNUHR-UCD.png 720w,\n/static/e4dc33ec40ab4c8d7737c173ea116ad1/8c857/KNUHR-UCD.png 761w\"\n        sizes=\"(max-width: 720px) 100vw, 720px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n    </span></p>\n<center>[Usecase Diagram]</center>  \n<br/>\n<p>회원가입은 이메일 인증을 거쳐야 가능하기 때문에 <code class=\"language-text\">include</code> 관계로 표현했고,<br>\n신청자가 모임 참가 신청을 해야 채팅이 가능하고 채팅을 거친 다음에 모임장이 참가 신청에 대한 수락/거절을 하기 때문에 이 과정을 <code class=\"language-text\">include</code> 관계로 표현했다.<br>\n또한, 나머지 수정/삭제 기능들은 조회에 대한 추가 기능이기 때문에 <code class=\"language-text\">extend</code> 관계로 표현했다.</p>\n<p>아직까지도 <strong>Usecase Diagram</strong>은 헷갈리는 부분이 많다.<br>\n특히 <code class=\"language-text\">include</code>, <code class=\"language-text\">extend</code> 관계 부분이 헷갈리는데, 소프트웨어 설계 수업을 들을 때도 그랬지만 다이어그램을 그리고 제대로 된 정답을 알 수가 없어서 잘 모르겠다,,<br>\n그래도 나름 팀원들과 머리를 맞대서 그려봤는데 <code class=\"language-text\">include</code> 부분은 주변 다른 사람들의 조언도 구해서 맞는지 한번 더 확인해봐야겠다.</p>\n<hr>\n<h2 id=\"-er-diagram\" style=\"position:relative;\"><a href=\"#-er-diagram\" aria-label=\" er diagram permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔗 ER Diagram</h2>\n<p>처음부터 바로 <strong>ER Diagram</strong>을 그릴 수도 있지만, DB 수업 때 배운 방식대로 <strong>Entity</strong>와 <strong>Relation</strong>을 생각해서 차근차근 그려나갔다.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 720px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 53.888888888888886%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC5ElEQVQoz01SWWgUQRCtXY+Iglc+REVkFYOiICoKIigRg4KsioqIAU+IIXigHwYRxI8EBT8UPECRoBIvPEAFL6II4hETs8lusutmj7m6e7p7pntmZw0mH2GlBxQburuoevWquvoBAEBs4XLQdAOKmgGVSgXu3nsElPGxCJM4JnaHaaGDpoVaMbGbGXemJXpTIU6tRF8K0pks3LrdDvsbjgAIISOeXwLP94+5QjQp4O69h6Ov372fhDDZ2pfsr6GMt2JMDhqmVVco6gsUpv3hk3Hqpozvc13R6Pm+4ojC9+6esEJRM2KabsZOn22JXLvRFp2/eCWMjo4Cwhh+ZnNz8wWtOgiCsLOLl65Gm46fiig7ncnOyfwcjHX9SMC3zi6AtgdPAWF74sb4ruivoSHQDROSqQHQDStM1nRzDONOC+O8vlDU4NfQ0BjlX7RsTRj/f6vmQDPMCRbCzcnUwOxcvrjEpuxQdjBXbVM2T4H8UjAZYbKJMb5OdcG4AxgT6E2mwhn+JUum0mBTBuo4oxvmgcdPnk1NZ7IrexK9NZpurHCFPK+ACOHNFsI7MbGbbMpOIkxmIExmff7aOTaMYzLdMKzqoFyuMi00WRHeAYAoxqSVMn5KgTAmVZy7M5Td0fF+muO41xl3mnXDqiWELpPSu9L5vXuKinu+f1kIeRMT0mBTVq8I33T/SKx9/uLlTN2wGgdz+dqipi90hbygEkwLbaOMn2PcaSQ2XY+w/e+ZCBNQPgvhOLHpLkpZTDmPWghvLxT1+IePnyZWKpXxxKZLKOMnhPSqurp7QEhvjxByqZQeuK6IKrL7jx7Dq7cdoUKS/elwto4rQOlIdQGO425ljG9R4C/futQ858Z31Ic/KqXXEJTLG6TnQakUREZGRiBf0KAv2a8kBQOZbIQ7bpQxHgFXyFCgQVBeLaS36u9zhodH4PfvYVW1zvdLLa4QZzGxa4hNVfGIIioFAUjPB1Uoly+GUvsDHZVQ94e0KQ0AAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"KNUHR-ERM.png\"\n        title=\"KNUHR-ERM.png\"\n        src=\"/static/997be4e669ef248a5e5eafcac7f5e607/37523/KNUHR-ERM.png\"\n        srcset=\"/static/997be4e669ef248a5e5eafcac7f5e607/e9ff0/KNUHR-ERM.png 180w,\n/static/997be4e669ef248a5e5eafcac7f5e607/f21e7/KNUHR-ERM.png 360w,\n/static/997be4e669ef248a5e5eafcac7f5e607/37523/KNUHR-ERM.png 720w,\n/static/997be4e669ef248a5e5eafcac7f5e607/302a4/KNUHR-ERM.png 1080w,\n/static/997be4e669ef248a5e5eafcac7f5e607/07a9c/KNUHR-ERM.png 1440w,\n/static/997be4e669ef248a5e5eafcac7f5e607/8a2f1/KNUHR-ERM.png 1751w\"\n        sizes=\"(max-width: 720px) 100vw, 720px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n    </span></p>\n<center>[ER Model]</center>  \n<br/>\n<p>우선 서비스에 필요한 기능을 바탕으로 필요한 <strong>Entity</strong>들을 추려냈고, 이 <strong>Entity</strong>들에 필요한 필드들을 정의한 다음에 위와 같은 <code class=\"language-text\">ER Model</code>을 그렸다.</p>\n<p>이후, <code class=\"language-text\">ER Model</code>을 바탕으로 <strong>Relational Mapping</strong> 과정 <a href=\"https://jfelog.netlify.app/db-er-to-relational/\">[Database] ER-to-Relational Mapping</a>을 거쳐 <strong>PK</strong>와 <strong>FK</strong>를 명시한 다음과 같은 <code class=\"language-text\">Relational Model</code>을 만들었다.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 720px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 70.55555555555556%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC7UlEQVQ4y41TTUgbQRidbdxGhCbiHz3EFHLwoJV61ragPXroyVOPVQraQvFQ6cUmthUr1FbRxCJKD9FDQsGLoCIoigf3J7sbN5tN9tvMrrsmqAjqQRSElFkqPUUdeLyZb2bezPu+GbS1tUWxLDuGMZ6zbXvSNM0IgWEYYdM0wxjj6XQ63ZDP51E4HKaKxSK6CYimae/09HTXyspKl2maLyzL6igUCu37+/vt+Xy+3bbtjpOTE28oFEJ3ajRNV29sbIzLshzVdf2XruuzBADgsKZps4ZhLABADGMc03XdYU3TYrlczmESu55HLperOhwOP43H4+3xePzZzMzM42g02sBx3CNJkvyiKPqTyWS9KIo+SZJ8hAVB8HEc50skEj6GYRzmed6JI6/XW5VMJoczmcwcAIwPDAxUXefj+PgYXVxcoMvLS3R1dYVIHnmeR5IkIY7jUCKRQAzDIEEQnLgoigi53e6qtbW1j5IkjaXT6c/9/f1VJBNtbW3u5uZmurGxkW5paSkLBAL3CchBS0tL1O7uLpVKpShJkhwmY0VRKEdwe3t7QFGUUVmWhxYXF59MTU09ZBimrFgsus7Pz12El5eXy2pra8vq6+uR3+8vXZSKiopqjuOCiqL8UFX1i67rv0VR7D07O0MAgHRdd3B4eEiWU7dWuby83LO6uhpkWXYSAL7KsjyqquqIpmmhXC4XUlWV8FAqlRqMRCLV8/PzKBaLlRakKMqzvr7eu7m5Gcxms+8B4A0AvM5msz0A0JPJZHpIH2P8amJiwhONRtHCwkJpQY/H493Z2RkSRTGiKMqkpmmNhUIBHRwcoKOjI2Tb9v9fcBfLlZWVD3ieD5qm+V1V1W8AMAwAnzDGQcMwBg3DGBEE4SUR7OzspMmeurq60oI1NTXlDMO8U1V1EGP8wbIsYrH7n9VuAHjLsuxzItja2krfesOmpqb7mqb9tCzrz97eXsyyrACxSqp8enrqgDzovr6+ewiRj+W6UfAvyOsLH5drkScAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"KNUHR-RM.png\"\n        title=\"KNUHR-RM.png\"\n        src=\"/static/b8b42bb32168a7c3fd027bd7c1a2dd88/37523/KNUHR-RM.png\"\n        srcset=\"/static/b8b42bb32168a7c3fd027bd7c1a2dd88/e9ff0/KNUHR-RM.png 180w,\n/static/b8b42bb32168a7c3fd027bd7c1a2dd88/f21e7/KNUHR-RM.png 360w,\n/static/b8b42bb32168a7c3fd027bd7c1a2dd88/37523/KNUHR-RM.png 720w,\n/static/b8b42bb32168a7c3fd027bd7c1a2dd88/302a4/KNUHR-RM.png 1080w,\n/static/b8b42bb32168a7c3fd027bd7c1a2dd88/c6ff8/KNUHR-RM.png 1088w\"\n        sizes=\"(max-width: 720px) 100vw, 720px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n    </span></p>\n<center>[Relational Model]</center>  \n<br/>\n<p>마지막으로 필요한 <code class=\"language-text\">주요 테이블 정보</code>들을 정리하고, 지금까지 설계한 내용들을 바탕으로 <code class=\"language-text\">ER Diagram</code>을 그리며 마무리했다.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 720px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 56.111111111111114%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB1UlEQVQoz22SyY7cMAxE9f9fl0OQQzLTCbotL9p3y1tXQM5MAwFigBAtS0XWM8X3YcGbXHDXDlMskD5hjgVjyJhCxm/tYcqKKRR8uz3w0O6f76PPH7lPuOkAEWOEUhohRuRcUGtDypmjrStyKR9rzlBaY5omPktB+6VU9N5RSkFdV4jndcF7B+csnHPYtw3OWt6j5/l8Yu0d+76zeAgBKSUsy4JEwiHgPE8+27cdovUNxjloY7AoBec9jLUwxnAHNkaU1nBdF2JKUErBe49hGLgB6m7bNo62dogfUuF9UsxQ2sAsiBHzcRE35TjHdbE9KkSYtNZsk8TZcs4otUIY5zFOM/Ox1jI7ukj2et/4UK4Nx3HAxYhpntnBPM/MkYRJsJFoaxDknzjEENjCF6P2aZOEjn3H4DPeFssuBht45XCR3x/G4005CIJtjOVKZIcY0UpWKP9zvzPLkAseUkJKySxra68pWPuGWitibRA/F/eqQDN2VxaD8Z+VPX5Nmhmex4EQE1vlibD2xZAmgWLfD4jZWMhxhBwn+BDhfECulTugcSF+tW+4rpM7Iyz0U8gFCVL+JUzfBV22zsFaw5VbqwjBM1PiaH1ga8d5srV1Xf8bISVu5C/ZM0ds/OqFMAAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"KNUHR-Entity.png\"\n        title=\"KNUHR-Entity.png\"\n        src=\"/static/119c928c15e0ef95b4c5130c7c8ac58d/37523/KNUHR-Entity.png\"\n        srcset=\"/static/119c928c15e0ef95b4c5130c7c8ac58d/e9ff0/KNUHR-Entity.png 180w,\n/static/119c928c15e0ef95b4c5130c7c8ac58d/f21e7/KNUHR-Entity.png 360w,\n/static/119c928c15e0ef95b4c5130c7c8ac58d/37523/KNUHR-Entity.png 720w,\n/static/119c928c15e0ef95b4c5130c7c8ac58d/302a4/KNUHR-Entity.png 1080w,\n/static/119c928c15e0ef95b4c5130c7c8ac58d/07a9c/KNUHR-Entity.png 1440w,\n/static/119c928c15e0ef95b4c5130c7c8ac58d/e8950/KNUHR-Entity.png 2000w\"\n        sizes=\"(max-width: 720px) 100vw, 720px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n    </span></p>\n<center>[주요 테이블 정보]</center>  \n<br/>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 720px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 107.22222222222221%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAYAAABG1c6oAAAACXBIWXMAABYlAAAWJQFJUiTwAAADUElEQVQ4y5WUe2/iOBTF+f7fZP/svndmpJ2qHTFMobSQAAkkAQKU8krI23n9VjZ9MJ2utGvpygb7npx7fK4bZVlSFAVv57qu2HpH/mre8dtVm0/tIS1zxaMXUlenM+/lNeTibciNqqqI0wR34fKwXGBPxkxtiyRJ1N57eTIa/8ZQJh2DAH04pK9p9Poahmni+b7a+98M67rGPwbcagZfb/u0+0O6gzHbg6fk+M8Mz6OqTofSNFWzEOLdc+8ylIdlYpZlP8xxHKv1eTzvy3gGVAzljzwvlNgizymK8iWoK8aLAxeXHS4u22jTR1XuOYAEzvP8laE8oEZdngJUqXKTusacOFy32ly3Olgz96zkkuIJUIj8leE+SGj2xjT7YwZLj8FiTxinVGWpbnO3PzCaOOjGhMXDmrqqyJ/0hZqqyOFprRhudns+XLe5utXpGDN61lIBysRM5Ky9GHfrszpErP2EJBPK2GEimG+OOOs9zvrAwyE8AUZJzMyxmU9tJuaIwD8oZjJSIfjUuufnyw6fezbfxg8EUaJk2XgBF3+3+Nge0p0d0OY7MiFoBHHMeDLBsiwVURQRRpHSJopjNps1K3eGZRp4++2LG/yjz2rpMndsdps1eXbqoEb4BGjbDpPJBMeZvvguTlPMiYWu6/R6fW5vuwRBSCVNfzigaxr6YMDNTRvDME8lSzamYTA2TUbDIbPplCxNVadkmcB0FnR1g65u0u4NFHs5/GOIZjpopk3PsBhOHGW7xnbv0brTVXu1uhodbUQYxQpQlp9IEwtBXpQkaYZ3DJWORSl1rqlqFOO65rX10kwQJwlBFCk7GO6Onz5+5demzhdjjRfEyhZ+mPJHU+PPtsl840vDvrbek9kbUshnoWUk8tbnLvf3PRbLlWIrL0iOg+fxrdOlpw953O6UtZ5N/mJsZdC6pC5PZpUfcFcPdO763N73FYB8xmRNeS5wV2usqavkOAG+fW2qCnu54950SESuEuNM4McZfpRyjDPV6/L/rR9juhvmu1CZ/LyHXwBlH3740uX3qzbbw5FcCESWUeRCreUsSy6KnO7I4pfLG5rDOd3xQklV/QgosJ0pY8tmv9+f6Zl9N0tQd7liZI7pazojw/zufXx+bf4BCDI9chtZfGUAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"KNUHR-ERD.png\"\n        title=\"KNUHR-ERD.png\"\n        src=\"/static/665ba361ca1af003ef9436171a9ee51a/37523/KNUHR-ERD.png\"\n        srcset=\"/static/665ba361ca1af003ef9436171a9ee51a/e9ff0/KNUHR-ERD.png 180w,\n/static/665ba361ca1af003ef9436171a9ee51a/f21e7/KNUHR-ERD.png 360w,\n/static/665ba361ca1af003ef9436171a9ee51a/37523/KNUHR-ERD.png 720w,\n/static/665ba361ca1af003ef9436171a9ee51a/302a4/KNUHR-ERD.png 1080w,\n/static/665ba361ca1af003ef9436171a9ee51a/07a9c/KNUHR-ERD.png 1440w,\n/static/665ba361ca1af003ef9436171a9ee51a/913a1/KNUHR-ERD.png 1476w\"\n        sizes=\"(max-width: 720px) 100vw, 720px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n    </span></p>\n<center>[ER Diagram]</center>  \n<br/>\n<div class=\"table-of-contents\">\n<ul>\n<li><a href=\"#-%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD-%EC%A0%95%EC%9D%98-%EB%B0%8F-%EA%B8%B0%EB%8A%A5-%EC%84%A4%EA%B3%84\">🧾 요구사항 정의 및 기능 설계</a></li>\n<li><a href=\"#-usecase-diagram\">🙆 Usecase Diagram</a></li>\n<li><a href=\"#-er-diagram\">🔗 ER Diagram</a></li>\n</ul>\n</div>","excerpt":"학교 멘토링 프로그램으로 카카오 판교 아지트를 방문하고 자극을 받은 뒤, 2학기가 시작하기 직전인 8월 중순 무렵부터 새로운 프로젝트를 시작했다. 주제로 삼은 건 경북대 학생들을 위한 프로젝트/스터디 구인구직 사이트. 이미 우리 과에서는 7월 쯤부터 노션을 통해 학부생들을 위해 서비스를 제공하고 있었지만, 노션으로 사용하기에는 불편함을 느껴 웹으로 만들어보고 싶다는 욕심이 생겼다. 그렇게 새로운 프로젝트를 시작했고, 지금까지 진행한 프로젝트 진행 상황과 앞으로 있을 프로젝트 진행 상황에 대해서 포스팅 해볼 예정이다. 🧾 요구사항 정의 및 기능 설계 우선 사용자 요구사항 정의서를 작성하면서 프로젝트에 필요한 기능들을 생각했다.  회원가입/로그인 부분은 전체 서비스가 경북대 학생들을 위한 서비스이기 때문에  메일 인증을 거쳐서 회원가입을 하고 로그인 할 수 있도록 기능을 설계했다. 그 다음에는 서비스에서 가장 중요하고 없어서는 안되는 기능인 모임 CRUD와 참가 기능을 넣었다. 모임에…","frontmatter":{"date":"October 23, 2022","title":"[KNUHR] KNUHR 프로젝트 시작 & 설계","categories":"Project","author":"JFe","emoji":"📑"},"fields":{"slug":"/knuhr-design/"}},"next":{"id":"a19de102-1ba5-599b-8118-7db4c497f0fc","html":"<p>마지막 포스팅 이후 거의 세달 만에 다시 돌아왔다,,<br>\n자소서 쓰고 코테 치고 이것 저것 공부하다 보니 바빠서 블로그 관리할 생각을 못하고 있었다. 하지만 취준을 하다보니 지난 프로젝트를 되돌아보고 정리할 필요성을 느껴서 끝난지 조금 지난 프로젝트지만 늦게나마 정리해보려고 한다. (블로그도 다시 써야지)</p>\n<h2 id=\"-프로젝트-소개\" style=\"position:relative;\"><a href=\"#-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%86%8C%EA%B0%9C\" aria-label=\" 프로젝트 소개 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🎞 프로젝트 소개</h2>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 720px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 49.44444444444444%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAABYlAAAWJQFJUiTwAAACRUlEQVQoz13PS2/TWACGYf//NdKskNhUM0iAZkCDUAuU0gtJ2tzs2Mc+vp3Y8bFz7DhpG2hu8CIYNsMrPZtv91mHw54ffVnfY+Yly2VLY+bstw+s1/fc3t6x3W5/2mw27HY79vv9//zYDocDXw87rFIr0mzKXNkYHdLMM0b2BZPhWxqd0i5XNE1NXdcYY9gfDvzXt19gv9tyvzLczSOsxaIkSwRRNETOEiZJwCRymWWSti6pKkOpK6qyROc5m4cv/N76YcNlGJFGPla90BSzAZE/4CqQDLIpQT5naTSmLMhzxWyWUFQlflaQz2vuVw1mkVC3De3dZ26XNdehx9i7wlrUGW72D13xGF9KQjUk0B+I1Smu+AtfXSDKF+SF5DyJOBMh3eA9veIRn6ZHhEUPpWPulgbHu8Zyg2uyPEBOX9J1RwjVQZTH3HhPOBdP8aIO7wbPuXIuSGLJxHf5e+jiTa955h3xWnboqymzQmBMjJVphRNLbN9hFl/Rmhg1DRHxJR3X4UadMHZf0hc2wyhH5R4fvCPOZcKJL3FTxbot0IWkqDysptEsFwV2IhiIS6psQGUUdTpiHHaQekA/DolCF6UkWREjxDFxOmGQ9PEKxcLMqOspVZlgrVrN55Vh0Wb8Oz4jLlwq5SCiV3xKXuBkf5JrB+G7DMc90thnIgUj6dEPT+jpI3QpiWKfKBZYx/Ypl26X995H+n6fMI04s4+5yf/ATt9wGjzmo7RJogl+0MeZdMiSMT23+/OuSkeIYIzrDfGDMd8BZ4zQHTzbH28AAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"CT-main.png\"\n        title=\"CT-main.png\"\n        src=\"/static/0b9699f313a1acff2b30d243109fae42/37523/CT-main.png\"\n        srcset=\"/static/0b9699f313a1acff2b30d243109fae42/e9ff0/CT-main.png 180w,\n/static/0b9699f313a1acff2b30d243109fae42/f21e7/CT-main.png 360w,\n/static/0b9699f313a1acff2b30d243109fae42/37523/CT-main.png 720w,\n/static/0b9699f313a1acff2b30d243109fae42/302a4/CT-main.png 1080w,\n/static/0b9699f313a1acff2b30d243109fae42/07a9c/CT-main.png 1440w,\n/static/0b9699f313a1acff2b30d243109fae42/d9ed5/CT-main.png 2880w\"\n        sizes=\"(max-width: 720px) 100vw, 720px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n    </span></p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 720px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 40.55555555555556%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABxklEQVQoz0WPyW4TURBF/Yf8DDvEF/ABrNiERcQiYsGCUcmCIEiMQyKFJB5it+eeX09+/abuA+6IUNLVVemqjm71WmdRdY1RiraxWGsxxnSutSHNKoK4YDLbMptuuBsvuRl6LDcpaSbJix3O/b/pHQ8WvP3mcXhyz+DOh9Z14V5BlFPtarSxBHFFEGzwllMW64SskFS7BFHsofIR2ntzMuHF0TXPX//i888FjX2AKW0ZTeb0z8+YzWaEqeFmNKR/e06UarZBxGB0jchLoqSgcQ9Fegefhjw7uODpqz4f+/OuYdM4lFIkoup2kUtWYUVd11hlyUtFmJQ4ayjLHVFa4v4BD4/Hjw3fn3lkuSTOJH4i8dYZUjk2ocbPA2b5b9a7CcPtlKKyNC0Y2yDyChqH2wP7tz5Hp3Pe/VhyPY2wWnfhTsTEoqIFlHZYZ7GNQVuJazXaOPJKIwrFcBqT1gahLD0pFbJ27CcTJd/vK76efsB7+YTJ/RKpIU4VX1YeV0nIKBOdJ7JG65ZQ1JxfrrnMNBeFoRcmBeN5ircWpKIkKA1X4zHJuM/KT9mERad5KFhFGcsoYx3n+HGJH5Ws/JyyqlHWUv99+Q/L8lSwrYkHLwAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"CT-structure.png\"\n        title=\"CT-structure.png\"\n        src=\"/static/e62b1a4368168998beb9906760e5955c/37523/CT-structure.png\"\n        srcset=\"/static/e62b1a4368168998beb9906760e5955c/e9ff0/CT-structure.png 180w,\n/static/e62b1a4368168998beb9906760e5955c/f21e7/CT-structure.png 360w,\n/static/e62b1a4368168998beb9906760e5955c/37523/CT-structure.png 720w,\n/static/e62b1a4368168998beb9906760e5955c/302a4/CT-structure.png 1080w,\n/static/e62b1a4368168998beb9906760e5955c/07a9c/CT-structure.png 1440w,\n/static/e62b1a4368168998beb9906760e5955c/555cf/CT-structure.png 1960w\"\n        sizes=\"(max-width: 720px) 100vw, 720px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n    </span></p>\n<p><a href=\"https://github.com/Go-Jaecheol/Carbon-Tracker\">https://github.com/Go-Jaecheol/Carbon-Tracker</a><br>\n<strong>Carbon-Tracker</strong>는 <code class=\"language-text\">대구 주택 단지별 탄소 배출량 및 탄소 포인트 예측/시각화</code>라는 주제의 프로젝트로, 종합설계프로젝트2 과목을 수강하면서 진행한 산학 협력 프로젝트다.</p>\n<p><strong>22.03 ~ 22.06</strong> 기간 동안 4명의 팀원이 크게 <strong>React</strong>를 사용한 프론트엔드 2명 / <strong>Spring Boot</strong>를 사용한 백엔드 2명으로 나뉘어 진행했고, 그 중 백엔드 파트를 담당했다.</p>\n<p>프로젝트는 국토 교통부에서 제공하는 공공데이터 API를 활용해 데이터를 수집하고, <strong>Elastic Stack</strong>을 사용해 데이터 처리/시각화하는 과정으로 진행했다. 또한 기상청에서 제공하는 공공데이터도 활용하여 하루 탄소 배출량 예측 모델을 만들고, 이를 <strong>FastAPI</strong>를 통해 서비스를 제공해주는 과정도 진행했다.</p>\n<hr>\n<h2 id=\"-프로젝트를-통해-배운-점\" style=\"position:relative;\"><a href=\"#-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-%ED%86%B5%ED%95%B4-%EB%B0%B0%EC%9A%B4-%EC%A0%90\" aria-label=\" 프로젝트를 통해 배운 점 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>👨‍💻 프로젝트를 통해 배운 점</h2>\n<h3 id=\"-elk-stack-kafka\" style=\"position:relative;\"><a href=\"#-elk-stack-kafka\" aria-label=\" elk stack kafka permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>✅ ELK Stack‼ Kafka‼</h3>\n<p>이번 프로젝트를 통해 가장 큰 경험을 한 부분이라고 할 수 있다.<br>\n<strong>Elastic Stack</strong>과 <strong>Kafka</strong>라는 기술들에 대해 하나도 모르는 상태에서 직접 찾아보고 시행착오를 겪으면서 사용해보았다는 점에서 좋은 경험이 되었다고 생각한다.</p>\n<p>처음에는 <strong>공공데이터 API</strong>에서 받은 정보들을 <strong>csv</strong> 파일로 저장해서 <strong>Beats</strong>로 읽고 <strong>Logstash</strong>를 통해 데이터 처리 후 <strong>Elasticsearch</strong>에 보내서 데이터를 저장하도록 해야겠다고 생각하고 구현했다. 이 과정에서도 클라우드 환경이 아닌 <strong>Mac OS</strong>에 <strong>Elastic Stack</strong>을 설치하는 과정부터 제대로 정리된 글들이 많지 않아서 시간이 걸렸고, 특히 <strong>xml</strong> 형식으로 제공하는 공공데이터 API의 특성상 어떻게 데이터를 처리해야 하는지 알기 어려워 고생했어서 이 과정들을 추후 확인하고 다른 사람들에게도 공유하기 위해 블로그에 정리해뒀다.<br>\n<a href=\"https://jfelog.netlify.app/elastic-mac-install/\">[Elastic] Elastic Stack(ELK Stack) - Mac OS 설치 방법</a><br>\n<a href=\"https://jfelog.netlify.app/elastic-open-data-api/\">[Elastic] Elastic Stack + 공공데이터 api 활용</a></p>\n<p>종합설계프로젝트 과목의 특성상 다른 중소기업과 같이 연계하여 프로젝트를 진행하기 때문에 해당 기업의 요청사항에 맞게 구현해야 했는데, <strong>Elastic Stack</strong>과 <strong>Kafka</strong>의 사용 이유도 이 때문이다. <strong>Kafka</strong>를 사용해달라는 요청을 받고 어떻게 해야할지 고민하다가 문득 <strong>xml</strong>형식을 <strong>csv</strong> 파일로 변환하고, 이를 <strong>Beats</strong>가 읽어서 <strong>Logstash</strong>에서 다시 데이터 처리를 한다는 점이 불필요하다는 생각이 들었다. csv 파일 변환이 과연 필요할까..??<br>\n그래서 공공데이터 API에서 반환한 <strong>xml</strong> 데이터를 <strong>json</strong> 형태로 <strong>Kafka</strong>에 보내고, <strong>Logstash</strong>가 <strong>topic</strong>별로 데이터를 처리해서 <strong>Elasticsearch</strong>에 저장하는 방식으로 변경했다.</p>\n<div class=\"gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">input <span class=\"token punctuation\">{</span>\n  kafka <span class=\"token punctuation\">{</span>\n    bootstrap_servers <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token string\">\"localhost:9092\"</span>\n    group_id <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token string\">\"carbon\"</span>\n    topics <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"energy\"</span>, <span class=\"token string\">\"apt\"</span><span class=\"token punctuation\">]</span>\n    consumer_threads <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token number\">2</span>\n    decorate_events <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token boolean\">true</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\nfilter <span class=\"token punctuation\">{</span>\n  json <span class=\"token punctuation\">{</span>\n    <span class=\"token builtin class-name\">source</span> <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token string\">\"message\"</span>\n  <span class=\"token punctuation\">}</span>\n  mutate <span class=\"token punctuation\">{</span>\n    add_field <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token punctuation\">{</span><span class=\"token string\">\"[@metadata][index]\"</span> <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token string\">\"%{[@metadata][kafka][topic]}\"</span><span class=\"token punctuation\">}</span>\n    remove_field <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"ecs\"</span>, <span class=\"token string\">\"host\"</span>, <span class=\"token string\">\"@version\"</span>, <span class=\"token string\">\"agent\"</span>, <span class=\"token string\">\"log\"</span>, <span class=\"token string\">\"tags\"</span>, <span class=\"token string\">\"input\"</span>, <span class=\"token string\">\"message\"</span><span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">}</span>\n\n  mutate <span class=\"token punctuation\">{</span>\n    convert <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token punctuation\">{</span>\n      <span class=\"token string\">\"helect\"</span> <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token string\">\"integer\"</span>\n      <span class=\"token string\">\"hgas\"</span> <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token string\">\"integer\"</span>\n      <span class=\"token string\">\"hwaterCool\"</span> <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token string\">\"integer\"</span>\n    <span class=\"token punctuation\">}</span>\n    remove_field <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token punctuation\">[</span> <span class=\"token string\">\"@timestamp\"</span> <span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\noutput <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">[</span>@metadata<span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span>kafka<span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span>topic<span class=\"token punctuation\">]</span> <span class=\"token operator\">==</span> <span class=\"token string\">\"energy\"</span> <span class=\"token punctuation\">{</span>\n    elasticsearch <span class=\"token punctuation\">{</span>\n      hosts <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"http://localhost:9200\"</span><span class=\"token punctuation\">]</span>\n      index <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token string\">\"energy\"</span>\n      document_id <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token string\">\"%{kaptCode}%{date}\"</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">[</span>@metadata<span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span>kafka<span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span>topic<span class=\"token punctuation\">]</span> <span class=\"token operator\">==</span> <span class=\"token string\">\"apt\"</span> <span class=\"token punctuation\">{</span>\n    elasticsearch <span class=\"token punctuation\">{</span>\n      hosts <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"http://localhost:9200\"</span><span class=\"token punctuation\">]</span>\n      index <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token string\">\"apt\"</span>\n      document_id <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token string\">\"%{kaptCode}\"</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    elasticsearch <span class=\"token punctuation\">{</span>\n      hosts <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"http://localhost:9200\"</span><span class=\"token punctuation\">]</span>\n      index <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token string\">\"kafka-%{[@metadata][index]}\"</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<center>[그래서 바꾼 `logstash.conf` 설정 파일]</center>  \n<p><code class=\"language-text\">input</code> 부분을 <strong>beats</strong>에서 <strong>kafka</strong>로 바꾸고, 해당 kafka 서버를 설정하고 원하는 <strong>group</strong>, <strong>topic</strong>들을 설정했다.<br>\n<code class=\"language-text\">filter</code> 설정에서 <strong>json</strong>으로 받은 message에서 원하는 필드를 추가/삭제하고, 데이터 형도 알맞게 설정했다.<br>\n처리한 데이터는 <strong>Elasticsearch</strong>로 보내야 하기 때문에 각각 <strong>topic</strong>에 맞는 <strong>index</strong>에 위치하도록 <code class=\"language-text\">output</code>을 설정했다.</p>\n<hr>\n<h3 id=\"-머신러닝과-fastapi\" style=\"position:relative;\"><a href=\"#-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D%EA%B3%BC-fastapi\" aria-label=\" 머신러닝과 fastapi permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>✅ 머신러닝과 FastAPI</h3>\n<p>프로젝트 설계 초기부터 가능하면 기상청 공공데이터 API를 통해 기상 정보를 제공 받아서 에너지 사용량을 예측해보자! 라는 의견이 나왔었고, 꼭 기한 내에 이 부분까지 완성하고 싶었다. 머신 러닝은 잘 모르지만 학교 수업 때 들은 내용들과 예전 대회에서 상 받을 때 썼던 모델들을 가지고 예측 모델을 만들어봤다.</p>\n<p>우선 에너지 사용량을 예측하기 위해 과거 날짜별 전기, 가스, 수도 사용량 데이터를 얻으려고 했는데, 여기서부터 문제가 생겼다.<br>\n수도 사용량은 과거 데이터를 제공해주지 않았고, 전기/가스 사용량도 월별 데이터만 제공해줘서 학습용 데이터가 많이 부족했다.<br>\n그래도 우선 이 데이터들과 기상청에서 제공해주는 과거 기상 정보들로 필요한 데이터들을 전처리해주고, 해당 데이터들간의 상관 관계를 비교하여 학습 데이터로 사용했다.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 720px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 51.11111111111111%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAABYlAAAWJQFJUiTwAAAC4ElEQVQozwXBWUzTBwDA4f+bWab1YQYdM4v6oInJppksDCw0E+VYQzpBGNlgLDNKgkKhK6UIFKVABgUKKKUUVC45KpdXRBbFyVBQQBAoLRRpBY0BMUFBYYz+9n0CrNFjNJPvmYhBlsElWTp6WRr3pGq6JCpe9QzC+kdgnZGrHWQFn6Hidx2dUfm0/JrPaKSWnogc5oYs4FpFeNzQwTWNidGCPzF6hJDkFo7qizDyPg9Fs0mGpbEF/nvDeNcgN86WcTtCQcyG/Ug2eyMReRImOoBpUzBzbddZ/zCP0J5WQ+epVFi6T3tMKnGbQ1B/GUHSjkjkO6OxXK6G5RluVdyhVnoaWuspCYzmu41eHNnii8TtEJm7onldV4drwYnQoWumyiuEUVMZt3KqqY7SUHMiB3NYHLG7Q+nNuwAvbXQ2d6P+xIMeRTrd+gaaQtNoidTSHBRLzNc/M1h0ARw2hDuFTSi3hnF6o4yHxnr418nK/ARcz+W3byW0JhfDxBTD5m6kIjGST73p19fBqoOV12O4zFqO+wTSmVoC1imErgIzGvdwlO7hPCi6iOvtJIv2ftbLFMgP+NGoMvFxzMF48wMiRL5I3fzoza1k7Y2N9xN9LBfJUYiDuaauZGnUgXCjsAmF+zGU247xt+EKa68GeDdjwdWax3EPH0pVBhasLxhu6SZAJCboMwl9BTWszPaz5HzGcn02J8T+VKaUM29xIlh1DRjcjpKw/ReeKJVwWcFqqRxmHxHgJyP5pIbpyRkWr95DLvLk8LZAhuKUcCmRtfI/wNZFkP9RMuKymbU6EUa0teRuCELu/hPxe/1J+kZM4j4vfvxeSom+kCumWv7pfcrLhruUCr6otsg4ucefhH0Hif3Kmx98ArlYXkaVoYqBx8MIz//q56a6grbMagqTizmn0pOVUkxUVDz2sSEcdjuWCTtzI9MMaGrozW3kvNaE7pwRXaaR+Ph0ZqdsTFptTDtm+B8cVh7RfBmUTQAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"energy-corr.png\"\n        title=\"energy-corr.png\"\n        src=\"/static/fadc4fefbe7b206027ddea51e24c3366/37523/energy-corr.png\"\n        srcset=\"/static/fadc4fefbe7b206027ddea51e24c3366/e9ff0/energy-corr.png 180w,\n/static/fadc4fefbe7b206027ddea51e24c3366/f21e7/energy-corr.png 360w,\n/static/fadc4fefbe7b206027ddea51e24c3366/37523/energy-corr.png 720w,\n/static/fadc4fefbe7b206027ddea51e24c3366/302a4/energy-corr.png 1080w,\n/static/fadc4fefbe7b206027ddea51e24c3366/47218/energy-corr.png 1344w\"\n        sizes=\"(max-width: 720px) 100vw, 720px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n    </span></p>\n<p>상관 관계를 비교하여 선정한 feature들로 train 데이터들을 만들고 train 데이터와 test 데이터를 나누었다.<br>\n그리고 학습에 필요한 데이터가 부족하다고 생각해서 <strong>K-Fold 교차 검증</strong>을 진행해야겠다는 생각이 들었다.<br>\n학습에는 그나마 대회에서 썼을 때 좋은 결과를 냈던 <strong>XGBoost</strong> 알고리즘으로 학습해봤고, <strong>RandomizedSearchCV</strong>로 학습에 적합한 하이퍼 파라미터를 찾아서 학습을 진행했다.</p>\n<p>결과는 만족스럽지 못했다,, 85퍼센트 정도의 정확도로 학습 데이터가 부족하기 때문에 어느정도 예상했던 결과였지만, 이상치를 더 확실하게 제거하는 등 전처리를 더 잘하거나 더 적합한 하이퍼 파라미터를 찾았다면 어땠을까 아쉬움이 남았다..</p>\n<p>예측 모델을 서빙하기 위한 프레임워크로는 <strong>FastAPI</strong>를 선택했다.<br>\n우선 기한 내에 빠르게 구현하기 위해 그래도 익숙한 웹 프레임워크 중에서 고민을 했고, 파이썬 웹 프레임워크들 중에서도 <strong>FastAPI</strong>가 <strong>Django</strong>, <strong>Flask</strong>보다 더 경량화 되고 빠르다고 최근 주목받고 있어서 선택했다.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 720px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 26.111111111111107%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAh0lEQVQY06XOvQrCMBiF4dz/5bi5ujkpiIODtEiLTfqT6pcmTQp5Rb0C6wvPdoajGtNRlDVVU+Ki5l3OeTUVfWKZF/4uf56gDm3Bub0hwWO9Q1LApRn5wXcfkBhQG3Nkez9RdYZi0NQyYOYnegXjH6hdf2HfXxmcoN1I5wWbPDZO2PSbMU68AEfogxjcerT7AAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"CT-FastAPI.png\"\n        title=\"CT-FastAPI.png\"\n        src=\"/static/bf2fc7986e987de503d10ba9008fe219/37523/CT-FastAPI.png\"\n        srcset=\"/static/bf2fc7986e987de503d10ba9008fe219/e9ff0/CT-FastAPI.png 180w,\n/static/bf2fc7986e987de503d10ba9008fe219/f21e7/CT-FastAPI.png 360w,\n/static/bf2fc7986e987de503d10ba9008fe219/37523/CT-FastAPI.png 720w,\n/static/bf2fc7986e987de503d10ba9008fe219/302a4/CT-FastAPI.png 1080w,\n/static/bf2fc7986e987de503d10ba9008fe219/07a9c/CT-FastAPI.png 1440w,\n/static/bf2fc7986e987de503d10ba9008fe219/92a6b/CT-FastAPI.png 2334w\"\n        sizes=\"(max-width: 720px) 100vw, 720px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n    </span></p>\n<p><strong>FastAPI</strong>를 사용해보고 나서 왜 이름에 <strong>Fast</strong>가 붙었는지 알 것 같았다. 구현이 빠르다. 디렉토리 구조도 간단하다. 심플하다.<br>\n파이썬 프레임워크를 처음 사용해서 패키지 버전 관리에서 살짝 버벅였지만 이는 <code class=\"language-text\">requirements.txt</code>를 이용하여 빠르게 해결했고, 코테에서 써서 익숙한 파이썬이기도 하고 전체적으로도 예측 결과만 제공해주면 됐기 때문에 큰 이슈없이 구현할 수 있었다.</p>\n<hr>\n<h3 id=\"-협업을-위한-git-convention\" style=\"position:relative;\"><a href=\"#-%ED%98%91%EC%97%85%EC%9D%84-%EC%9C%84%ED%95%9C-git-convention\" aria-label=\" 협업을 위한 git convention permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>✅ 협업을 위한 Git Convention</h3>\n<p>이전에 진행했던 프로젝트에서는 특별한 <strong>convention</strong> 없이 <strong>commit</strong>하고 <strong>push</strong>하고 프로젝트를 진행했다. 하지만 협업을 하면서 이런 규칙 없는 정리되지 않은 방식은 불편함을 느낄 수 있었고, 그래서 프로젝트 시작 전부터 미리 규칙을 정한 후 진행했다.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 720px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 67.77777777777777%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAABYlAAAWJQFJUiTwAAABiElEQVQ4y52S23LbIBRF/Sf1RTdLSCAESLYF8iXxJG0y/f+vWR3hTKZPtd0H3mCx9jl7sRU9Zn9B2pFK9ajxzPDzN6IPLFPBppCs8+bhs8jKDmk9q6xik9c01qN2R5rdEeECeW1YPwFd5JVBDyfSUpFVLcoFpBmxl1+410+Kxj5nmFcWaQOltBS1oe0n1BAo9gO5cSSFYpXV32ed3Y1sYuS/DVUfEINHuJFS9WSiI600iWjZlPJ+5LY/UtRdhM7Abn+mtQHR7pDdiHYTcvBs/Y5MG1ZpfQfopggsRBeB7WxpPbU9kO8cSatZJhWrpP4n7AYsDcqGaJduJbIP6MOFxnny2VreosYHxYNbnq3iDEtFezjhrh/UQ+DHbJV+LePxHt4Mc6FjbOU82r9gLu8Uqn8K9l1sofdsijpayq9iq3BFWE9W6f/oofHRLp+XYj3SebrzO/r0RlK2zwINjRkjbC62dAE9vSIPZ9JqrlL7fGTlJlZpyTLZxi130xU5vlDIIX4yX3x0ln8A5NapOLa5h/wAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"CT-issue.png\"\n        title=\"CT-issue.png\"\n        src=\"/static/a40d69604521d5d206e1d7cb8ce10b53/37523/CT-issue.png\"\n        srcset=\"/static/a40d69604521d5d206e1d7cb8ce10b53/e9ff0/CT-issue.png 180w,\n/static/a40d69604521d5d206e1d7cb8ce10b53/f21e7/CT-issue.png 360w,\n/static/a40d69604521d5d206e1d7cb8ce10b53/37523/CT-issue.png 720w,\n/static/a40d69604521d5d206e1d7cb8ce10b53/302a4/CT-issue.png 1080w,\n/static/a40d69604521d5d206e1d7cb8ce10b53/07a9c/CT-issue.png 1440w,\n/static/a40d69604521d5d206e1d7cb8ce10b53/6c86f/CT-issue.png 1720w\"\n        sizes=\"(max-width: 720px) 100vw, 720px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n    </span></p>\n<p>우선 <strong>Github issue</strong>를 활용해 구현이 필요한 이슈 사항을 작성하고 해당하는 label을 달아서 issue를 만들었다.<br>\n이후 해당하는 <code class=\"language-text\">label/issue번호</code>로 <strong>branch</strong>를 만들어서 작업하고, [<code class=\"language-text\">#해당 issue 번호</code> <code class=\"language-text\">label</code>: 구현 내용] 형식으로 <strong>commit message</strong>를 작성하도록 했다.<br>\n<strong>push</strong> 한 후에는 <strong>Pull Request</strong>를 만들어서 협업하는 팀원이 확인할 수 있도록 했고, 확인했으면 <strong>merge</strong> 하는 방식으로 진행했다.</p>\n<hr>\n<h2 id=\"-keep\" style=\"position:relative;\"><a href=\"#-keep\" aria-label=\" keep permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔒 Keep</h2>\n<ul>\n<li><strong>Git Convention</strong>을 정해두고 <strong>branch/issue</strong> 나누어서 협업하는 방식</li>\n<li>새로운 기술에 대한 도전 (더 많이 새로운 걸 경험해보자!)</li>\n</ul>\n<h2 id=\"-problem\" style=\"position:relative;\"><a href=\"#-problem\" aria-label=\" problem permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🚧 Problem</h2>\n<ul>\n<li>새로운 기술 겉핥기 (새로운 경험을 한다는 건 좋은 시도지만 더 파고들어보자!)</li>\n<li><strong>Spring</strong> 경험 부족 (Spring을 사용해보기 위해 처음 진행한 백엔드 프로젝트였지만 너무 다른 기술들과 인프라 쪽에 치우쳐져서 진행한 것 같다,,)</li>\n<li><strong>테스트 코드</strong> 작성 필요 (테스트 코드 작성이 주는 장점은 알고 있다. 그럼 해보자!)</li>\n</ul>\n<h2 id=\"-try\" style=\"position:relative;\"><a href=\"#-try\" aria-label=\" try permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🎯 Try</h2>\n<ul>\n<li><strong>ELK Stack</strong>, <strong>Kafka</strong>를 제대로 활용한 프로젝트를 진행해보자</li>\n<li><strong>Spring Boot</strong>를 이용한 프로젝트를 더 많이 진행해보자</li>\n<li><strong>Junit</strong>를 이용해 단위 테스트 코드를 작성하며 구현해보자</li>\n</ul>\n<div class=\"table-of-contents\">\n<ul>\n<li>\n<p><a href=\"#-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%86%8C%EA%B0%9C\">🎞 프로젝트 소개</a></p>\n</li>\n<li>\n<p><a href=\"#-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-%ED%86%B5%ED%95%B4-%EB%B0%B0%EC%9A%B4-%EC%A0%90\">👨‍💻 프로젝트를 통해 배운 점</a></p>\n<ul>\n<li><a href=\"#-elk-stack-kafka\">✅ ELK Stack‼ Kafka‼</a></li>\n<li><a href=\"#-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D%EA%B3%BC-fastapi\">✅ 머신러닝과 FastAPI</a></li>\n<li><a href=\"#-%ED%98%91%EC%97%85%EC%9D%84-%EC%9C%84%ED%95%9C-git-convention\">✅ 협업을 위한 Git Convention</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"#-keep\">🔒 Keep</a></p>\n</li>\n<li>\n<p><a href=\"#-problem\">🚧 Problem</a></p>\n</li>\n<li>\n<p><a href=\"#-try\">🎯 Try</a></p>\n</li>\n</ul>\n</div>","frontmatter":{"date":"October 14, 2022","title":"[Carbon-Tracker] Carbon-Tracker 프로젝트 회고","categories":"Project","author":"JFe","emoji":"🌲"},"fields":{"slug":"/project-carbon-tracker/"}},"prev":{"id":"389be15a-11bc-5d94-badb-af6c3a17dab8","html":"<p>지난 10월 26일부터 우테코 프리코스를 시작했다.<br>\n우아한테크코스 5기에 최종 합격하는 것을 목표로 지원했지만, 아직 자바에 익숙하지 않은 나로서는 프리코스만으로도 많은 것들을 얻어갈 수 있을 거라 생각했다.</p>\n<p>1주차와 2주차를 진행하는 시기에는 다른 면접들이 많이 잡혀있었어서 빠르게 구현만 하고 회고를 쓸 생각은 하지도 못했다.<br>\n하지만 3주차 미션을 다 구현하고 나서는 배운 점들을 꼭 회고로 남겨야겠다는 생각이 들었고, 온보딩이었던 1주차를 제외하고 2주차부터 다시 복기해가며 회고록을 써볼 생각이다.</p>\n<h2 id=\"-2주차-미션-소개\" style=\"position:relative;\"><a href=\"#-2%EC%A3%BC%EC%B0%A8-%EB%AF%B8%EC%85%98-%EC%86%8C%EA%B0%9C\" aria-label=\" 2주차 미션 소개 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🎞 2주차 미션 소개</h2>\n<p><a href=\"https://github.com/woowacourse-precourse/java-baseball\">2주차 미션 소개</a></p>\n<p>🚀 기능 요구 사항</p>\n<p>기본적으로 1부터 9까지 서로 다른 수로 이루어진 3자리의 수를 맞추는 게임이다.</p>\n<ul>\n<li>같은 수가 같은 자리에 있으면 스트라이크, 다른 자리에 있으면 볼, 같은 수가 전혀 없으면 낫싱이란 힌트를 얻고, 그 힌트를 이용해서 먼저 상대방(컴퓨터)의 수를 맞추면 승리한다.\n<ul>\n<li>예) 상대방(컴퓨터)의 수가 425일 때\n<ul>\n<li>123을 제시한 경우 : 1스트라이크</li>\n<li>456을 제시한 경우 : 1볼 1스트라이크</li>\n<li>789를 제시한 경우 : 낫싱</li>\n</ul>\n</li>\n</ul>\n</li>\n<li>위 숫자 야구 게임에서 상대방의 역할을 컴퓨터가 한다. 컴퓨터는 1에서 9까지 서로 다른 임의의 수 3개를 선택한다. 게임 플레이어는 컴퓨터가 생각하고 있는 서로 다른 3개의 숫자를 입력하고, 컴퓨터는 입력한 숫자에 대한\n결과를 출력한다.</li>\n<li>이 같은 과정을 반복해 컴퓨터가 선택한 3개의 숫자를 모두 맞히면 게임이 종료된다.</li>\n<li>게임을 종료한 후 게임을 다시 시작하거나 완전히 종료할 수 있다.</li>\n<li>사용자가 잘못된 값을 입력할 경우 <code class=\"language-text\">IllegalArgumentException</code>을 발생시킨 후 애플리케이션은 종료되어야 한다.</li>\n</ul>\n<hr>\n<h2 id=\"-이번-미션을-통해-배운-점\" style=\"position:relative;\"><a href=\"#-%EC%9D%B4%EB%B2%88-%EB%AF%B8%EC%85%98%EC%9D%84-%ED%86%B5%ED%95%B4-%EB%B0%B0%EC%9A%B4-%EC%A0%90\" aria-label=\" 이번 미션을 통해 배운 점 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>👨‍💻 이번 미션을 통해 배운 점</h2>\n<h3 id=\"-필요한-기능-분리\" style=\"position:relative;\"><a href=\"#-%ED%95%84%EC%9A%94%ED%95%9C-%EA%B8%B0%EB%8A%A5-%EB%B6%84%EB%A6%AC\" aria-label=\" 필요한 기능 분리 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>✅ 필요한 기능 분리</h3>\n<p>이전 1주차 온보딩에서도 문제 요구사항에 따라 필요한 기능을 나누고 구현했지만, 1주차 미션은 코딩 테스트 같은 형태의 문제여서 기능 분리에 크게 집중하지 않았다.</p>\n<p>하지만 이번 2주차 미션은 숫자 야구라는 하나의 게임을 구현하는 미션이어서 기능을 분리하는 것에 보다 집중했다.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 720px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 36.11111111111111%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAABYlAAAWJQFJUiTwAAAA0ElEQVQoz5WR3RKCIBCFfZKmVBREUUCRNMP+pvd/otNAU5M3ZRcHdnbgm7NnI6sMRneHdXeQQiLOxVsJrRHT+nl/9L8pooWENEfU3YRKD6CiQ8Y18lKD1QZMGJRyD8IVdln1G5gWCt14hd6f0A5nCD2i0iMa40CrdgHx9S9FW1IGB9I46OEcwNrO4I1FyprF4xf0q0N/eEceJHsX3GZcYZMw7Ei5gKwaOWENVD+jHS4BaKYbRHsAlzbkuAayAPoPfmRzuAb5HP1iUibDdv8FPgAB/dNaqMp43AAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"workflow.png\"\n        title=\"workflow.png\"\n        src=\"/static/ee1997ae4aae8a779263fc5c6a46433f/37523/workflow.png\"\n        srcset=\"/static/ee1997ae4aae8a779263fc5c6a46433f/e9ff0/workflow.png 180w,\n/static/ee1997ae4aae8a779263fc5c6a46433f/f21e7/workflow.png 360w,\n/static/ee1997ae4aae8a779263fc5c6a46433f/37523/workflow.png 720w,\n/static/ee1997ae4aae8a779263fc5c6a46433f/302a4/workflow.png 1080w,\n/static/ee1997ae4aae8a779263fc5c6a46433f/07a9c/workflow.png 1440w,\n/static/ee1997ae4aae8a779263fc5c6a46433f/54d4c/workflow.png 1571w\"\n        sizes=\"(max-width: 720px) 100vw, 720px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n    </span></p>\n<p>우선 미션에서 주어진 기능 요구 사항을 읽고 전체 흐름을 먼저 정리했다.<br>\n그 다음, 정리한 전체 흐름을 바탕으로 필요한 기능 목록을 뽑아내고, 하나씩 순서대로 기능을 구현하고 테스트 코드를 작성하는 방식으로 진행했다.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 720px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 23.888888888888886%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAl0lEQVQY052PWw6CMBBFuxQQCyItpdBWWx5GDBrd/36uoTXGmJg0fpxM5mPO3Eu0m2HGBQVXSGiFNOdIixqbPyGNmSDUgEpasNaBSYtt2QTxB9FCd75D+ZRX6P6CstbIdgJZ2Xgx3Uu/fz/4BbGnG8ywoJ8fOLykK0KPqOTRJ85ZF52SrDV56/xxmBa8c2BtECWUvWUx9Z8Za5dCWMDyZgAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"function-list.png\"\n        title=\"function-list.png\"\n        src=\"/static/ae18d8ce85c7447df0b7ca28599e8100/37523/function-list.png\"\n        srcset=\"/static/ae18d8ce85c7447df0b7ca28599e8100/e9ff0/function-list.png 180w,\n/static/ae18d8ce85c7447df0b7ca28599e8100/f21e7/function-list.png 360w,\n/static/ae18d8ce85c7447df0b7ca28599e8100/37523/function-list.png 720w,\n/static/ae18d8ce85c7447df0b7ca28599e8100/302a4/function-list.png 1080w,\n/static/ae18d8ce85c7447df0b7ca28599e8100/07a9c/function-list.png 1440w,\n/static/ae18d8ce85c7447df0b7ca28599e8100/e405b/function-list.png 1566w\"\n        sizes=\"(max-width: 720px) 100vw, 720px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n    </span></p>\n<p>기능을 미리 정해두고 맞춰서 구현해서 커밋하는 방식으로 하긴 했지만, 위에서도 보이는 것처럼 아직 기능이 가장 작은 단위로 분리되지는 않았고 유효성 검사에 대한 기능도 적은 상태다.</p>\n<p>가령 위의 경우에서 <code class=\"language-text\">입력받은 숫자와 컴퓨터 숫자 비교해서 볼, 스트라이크 반환하는 기능</code> 같은 경우에는 <code class=\"language-text\">숫자를 비교하는 기능</code>과 <code class=\"language-text\">볼, 스트라이크 반환하는 기능</code>으로 나눌 수 있을 것이다.</p>\n<p>이런 면에서 2주차 미션에서는 필요한 기능을 분리하려고 시도는 했으나,, 아직 완전히 만족스러운 형태로 작게 분리하지는 못해서 아쉬움이 남았었다,,</p>\n<hr>\n<h3 id=\"-이름을-통해-의도-드러내기\" style=\"position:relative;\"><a href=\"#-%EC%9D%B4%EB%A6%84%EC%9D%84-%ED%86%B5%ED%95%B4-%EC%9D%98%EB%8F%84-%EB%93%9C%EB%9F%AC%EB%82%B4%EA%B8%B0\" aria-label=\" 이름을 통해 의도 드러내기 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>✅ 이름을 통해 의도 드러내기</h3>\n<p>1주차 미션 공통 피드백 중 하나로 <code class=\"language-text\">이름을 통해 의도를 드러낸다</code>와 <code class=\"language-text\">축약하지 않는다</code>가 있었다.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 720px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 63.888888888888886%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABYlAAAWJQFJUiTwAAABcUlEQVQ4y42TSYvCQBCF8///jzcPgiLiSSHeFLfEuMd91zd8NVRoxJEJPF51dXXtiUqlksrlshqNhtrttiqVilqtlprNpjqdjv77vV4v46jb7arf72s4HGo8HmswGJjc6/WUJIkOh4N2u532+71Op5MB3fF41Pl8LtidRrPZTKvVSovFQsjz+VyTyUTT6dTOyAQCnLMss4AERuYeEMgyJAsuYR65MxhdmqYFuzwajYzzPC+yv9/vvw4fj4cul4uBlF2+Xq8WFZmSOPsd4N17D5/Pp6Jqtap6va44jlWr1bRcLv9seggef0KUJomlT+9g+rnZbKyU7XZr8ns23z4rmfpvt5shLNGniN4zwD5k4CtjU+YhZTJlmEZ7luv12oCeCcPcUQ32yNhjUwyFjDAISwzLdscezB2EgZGpwhxSFtE9osMfeWYEdQbYsOBsgpdc9BCn4UqE8H76CnmvPw3KekhTabz/Yh4VXcjocfhtlcjwB/VF5B2RZUnlAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"naming.png\"\n        title=\"naming.png\"\n        src=\"/static/e6543ed27860338dd7055cf781c67cfc/37523/naming.png\"\n        srcset=\"/static/e6543ed27860338dd7055cf781c67cfc/e9ff0/naming.png 180w,\n/static/e6543ed27860338dd7055cf781c67cfc/f21e7/naming.png 360w,\n/static/e6543ed27860338dd7055cf781c67cfc/37523/naming.png 720w,\n/static/e6543ed27860338dd7055cf781c67cfc/914c7/naming.png 978w\"\n        sizes=\"(max-width: 720px) 100vw, 720px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n    </span></p>\n<p>기존 1주차 미션이나 평소 코딩 스타일에서도 메서드 명이나 변수 명으로 그 메서드와 변수의 역할을 드러내려고 했었는데, 이 피드백에서 집중했던 점은 <code class=\"language-text\">축약하지 않는다</code>였다.</p>\n<p>평소에는 예를 들어, 사용자 상세 정보를 저장하는 변수면 <code class=\"language-text\">userInfo</code>로 변수 명을 정했었다. 당연히 이렇게 변수의 역할은 드러내면서 최대한 간결하게 이름을 작성하는게 좋다고 생각했기 때문에 이번 피드백은 많이 와닿았다.<br>\n피드백에서 원하는 대로 변수 명을 고친다면 <code class=\"language-text\">userInformation</code>이 될 것이다.</p>\n<p>이름을 축약하는 게 당연하다고 생각했지만 사실 이것도 계속 긴 변수 명을 적기 싫었던 귀찮음에서 온 게 아닐까 다시 생각하게 됐다.<br>\n그래서 2주차 미션부터는 클래스 명, 메서드 명, 변수 명 전부 하나하나 신경써가며 이름을 지었다.<br>\n<strong>축약하지 않고, 최대한 그 역할을 드러내도록!</strong></p>\n<hr>\n<h3 id=\"-클래스-구조\" style=\"position:relative;\"><a href=\"#-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B5%AC%EC%A1%B0\" aria-label=\" 클래스 구조 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>✅ 클래스 구조</h3>\n<p>필요한 기능들을 정의하고 난 다음에는 전체 <strong>클래스 구조</strong>를 어떻게 설계할지 고민했다.<br>\n미션에서 요구하는 숫자야구 게임 자체가 복잡한 기능들이 필요한 게임이 아니기 때문에 간단하게 기능만 구현할까도 생각했지만, 오히려 간단한 기능들만 필요하다 보니 구조에 더 신경을 써야겠다고 생각했다.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 720px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 45.55555555555556%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAqUlEQVQoz6WSWw6CMBREWQ3SB72lJUSkQMWiEhJfMe5/J2MgxgXQj/k9OffOJLb0OIcJZK/gqkcmHTLZbk6Scgffj6iaGYIGsLyLA2bCIaUBqnuDigChhyjLFcjIQ/sPJPkoux+wAatvIPeCUD12wsUDd9UMWk42AbkJYGs5G4GLUVlPKPbzWgiPgP1/KG0AuQckHaHMCFmcIgz5Aaq9w7gntL1A6rjpfAHDGhyt+8Y/wAAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"structure-code.png\"\n        title=\"structure-code.png\"\n        src=\"/static/de7a7ff3be3774077455d70acd360bf7/37523/structure-code.png\"\n        srcset=\"/static/de7a7ff3be3774077455d70acd360bf7/e9ff0/structure-code.png 180w,\n/static/de7a7ff3be3774077455d70acd360bf7/f21e7/structure-code.png 360w,\n/static/de7a7ff3be3774077455d70acd360bf7/37523/structure-code.png 720w,\n/static/de7a7ff3be3774077455d70acd360bf7/302a4/structure-code.png 1080w,\n/static/de7a7ff3be3774077455d70acd360bf7/89048/structure-code.png 1242w\"\n        sizes=\"(max-width: 720px) 100vw, 720px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n    </span></p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 720px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 33.888888888888886%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAv0lEQVQoz6WR2Q6CMBBF+yduyNoWOtDQIoIYo4b//59rWoLGF9eHk5mHyZmbGUalRq73WIUC60j+DROFBqcGyy33Usfc/yQMUgXbX1F3Z8iqRa5bqLrzlSuLICnu8nnhK9hik0K3JzTDCNOfYfoL2uMIWe2eRFMC8RYWZoSqOUJQg0hUiKVGmte+hrzENlUPcSTewhKpYbopGZkDRLkDmR6ZsuBkEQv9ndANuluRHUBm8CKX0D8nyLAK+ccyJ7wBJAnScaJ7ZMQAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"structure.png\"\n        title=\"structure.png\"\n        src=\"/static/ab472932cdc7a96a7251ccbee2c14463/37523/structure.png\"\n        srcset=\"/static/ab472932cdc7a96a7251ccbee2c14463/e9ff0/structure.png 180w,\n/static/ab472932cdc7a96a7251ccbee2c14463/f21e7/structure.png 360w,\n/static/ab472932cdc7a96a7251ccbee2c14463/37523/structure.png 720w,\n/static/ab472932cdc7a96a7251ccbee2c14463/302a4/structure.png 1080w,\n/static/ab472932cdc7a96a7251ccbee2c14463/07a9c/structure.png 1440w,\n/static/ab472932cdc7a96a7251ccbee2c14463/2ed34/structure.png 1776w\"\n        sizes=\"(max-width: 720px) 100vw, 720px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n    </span></p>\n<p>처음 생각한 방식은 <code class=\"language-text\">MVC 패턴</code>이었다.<br>\n<strong>Spring Boot</strong>를 사용해 프로젝트를 진행하면서 <strong>MVC 패턴</strong>으로 구현한 경험이 있었기 때문에 <strong>MVC 패턴</strong>을 고려했다. 더군다나 저번 카카오 2차 코딩테스트를 준비하면서도 다들 나름 잘했다고 생각이 드는 사람들은 <strong>MVC 패턴</strong>으로 과제를 해결하려고 했던 점들을 보고 나도 저렇게 해봐야지 생각했기 때문에, 이번에는 <strong>MVC 패턴</strong>을 적용해봐야겠다는 생각이 들었다.</p>\n<p>하지만 <strong>MVC 패턴</strong>을 적용해서 클래스 구조를 설계하려고 하니 막막했다.<br>\n지금까지 알고 있는 방식은 <strong>MVC 패턴</strong>이라는 <code class=\"language-text\">디자인 패턴</code>에 대해서 알고 있는 것이 아니라, <strong>Spring</strong>에서 주로 사용하는 <code class=\"language-text\">클래스 구조</code>에 대해서만 알고 있었다는 걸 깨닫게 되었다.<br>\nMVC 패턴이니까 <strong>Model</strong> 부분은 <code class=\"language-text\">domain</code>이라는 패키지 명으로 만들고, <code class=\"language-text\">service</code> 패키지에서 필요한 기능들을 나누고 각 <strong>service</strong>를 호출할 <code class=\"language-text\">controller</code>까지는 음.. 굳이 안만들어도 되겠다고 생각했다. 그리고 <strong>View</strong> 부분도 콘솔 입출력만 필요하니까 굳이 따로 안만들어도 되겠다고 생각했고, 그 결과 위와 같은 구조가 만들어지게 되었다.</p>\n<p>음.. 다 지나고 생각해보니 내가 왜 저랬을까,,<br>\n모르면 찾아보고 했어도 되는데 클래스 구조 설계에 많은 시간을 낭비하자는 생각에 쫓겨 저런 구조를 만들어버렸다.<br>\n내가 지금까지 알고 있다고 생각했던 MVC 패턴은 그저 <strong>Spring</strong>에서 주로 사용하는 구조에 불과했다. <code class=\"language-text\">구조</code>만 알고 있었던거지 <code class=\"language-text\">패턴</code>은 모르고 있었다.<br>\n다행히도 2주차 미션을 끝내고 뭔가 이상하다는 생각이 들었고, 다음 미션부터는 생각을 조금씩 바꾸게 되었다.<br>\n나중에 작성할 3주차, 4주차 회고를 본다면 차츰차츰 나아지는 모습을 볼 수 있을 것이다..^^</p>\n<hr>\n<h2 id=\"-keep\" style=\"position:relative;\"><a href=\"#-keep\" aria-label=\" keep permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔒 Keep</h2>\n<ul>\n<li>클래스, 메서드, 변수 명을 지을 때는 <strong>축약하지 않고, 최대한 그 역할을 드러내도록</strong> 짓기</li>\n<li><code class=\"language-text\">기능 분리</code> -> <code class=\"language-text\">구조 설계</code> -> <code class=\"language-text\">구현</code> 순서</li>\n</ul>\n<h2 id=\"-problem\" style=\"position:relative;\"><a href=\"#-problem\" aria-label=\" problem permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🚧 Problem</h2>\n<ul>\n<li>기능을 여전히 <strong>더 작은 단위</strong>로 분리할 수 있음</li>\n<li><strong>유효성 검사</strong>에 대한 기능이 적음</li>\n<li><strong>MVC 패턴</strong>에 대해 잘 모르는 상태로 구조 설계</li>\n</ul>\n<h2 id=\"-try\" style=\"position:relative;\"><a href=\"#-try\" aria-label=\" try permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🎯 Try</h2>\n<ul>\n<li><strong>가장 작은 단위</strong>로 필요한 기능 전부 분리하기</li>\n<li>우선 <strong>MVC 패턴</strong>에 대해 공부하고, 제대로 구조 설계하기</li>\n</ul>\n<hr>\n<p><a href=\"https://github.com/Go-Jaecheol/java-baseball/tree/Go-Jaecheol\">구현 코드 확인</a></p>\n<div class=\"table-of-contents\">\n<ul>\n<li>\n<p><a href=\"#-2%EC%A3%BC%EC%B0%A8-%EB%AF%B8%EC%85%98-%EC%86%8C%EA%B0%9C\">🎞 2주차 미션 소개</a></p>\n</li>\n<li>\n<p><a href=\"#-%EC%9D%B4%EB%B2%88-%EB%AF%B8%EC%85%98%EC%9D%84-%ED%86%B5%ED%95%B4-%EB%B0%B0%EC%9A%B4-%EC%A0%90\">👨‍💻 이번 미션을 통해 배운 점</a></p>\n<ul>\n<li><a href=\"#-%ED%95%84%EC%9A%94%ED%95%9C-%EA%B8%B0%EB%8A%A5-%EB%B6%84%EB%A6%AC\">✅ 필요한 기능 분리</a></li>\n<li><a href=\"#-%EC%9D%B4%EB%A6%84%EC%9D%84-%ED%86%B5%ED%95%B4-%EC%9D%98%EB%8F%84-%EB%93%9C%EB%9F%AC%EB%82%B4%EA%B8%B0\">✅ 이름을 통해 의도 드러내기</a></li>\n<li><a href=\"#-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B5%AC%EC%A1%B0\">✅ 클래스 구조</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"#-keep\">🔒 Keep</a></p>\n</li>\n<li>\n<p><a href=\"#-problem\">🚧 Problem</a></p>\n</li>\n<li>\n<p><a href=\"#-try\">🎯 Try</a></p>\n</li>\n</ul>\n</div>","frontmatter":{"date":"November 26, 2022","title":"[우아한테크코스] 우테코 프리코스 2주차 회고","categories":"우테코","author":"JFe","emoji":"⚾"},"fields":{"slug":"/wooteco-pre-2/"}},"site":{"siteMetadata":{"siteUrl":"https://jfelog.netlify.app","comments":{"utterances":{"repo":"Go-Jaecheol/Jfe_Blog"}}}}},"pageContext":{"slug":"/knuhr-design/","nextSlug":"/project-carbon-tracker/","prevSlug":"/wooteco-pre-2/"}},
    "staticQueryHashes": ["1073350324","1956554647","2938748437"]}