3:I[5613,[],""] 5:I[1778,[],""] 6:I[2581,["250","static/chunks/250-9edeb9d2171db55c.js","185","static/chunks/app/layout-051d49746254c3f8.js"],""] 4:["slug","project/web/likelion-hackathon","c"] 0:["YFd_7cSmvIuy-EBayyLo2",[[["",{"children":[["slug","project/web/likelion-hackathon","c"],{"children":["__PAGE__?{\"slug\":[\"project\",\"web\",\"likelion-hackathon\"]}",{}]}]},"$undefined","$undefined",true],["",{"children":[["slug","project/web/likelion-hackathon","c"],{"children":["__PAGE__",{},["$L1","$L2",null]]},["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children","$4","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/b14273d44a3cc3aa.css","precedence":"next","crossOrigin":""}]]}]]},[null,["$","html",null,{"lang":"ko","children":[["$","head",null,{"children":["$","script",null,{"dangerouslySetInnerHTML":{"__html":"\n\t\t\t\t\tconst saved = window.localStorage.getItem(\"data-theme\");\n\t\t\t\t\tif (saved) {\n\t\t\t\t\t\tif (saved === \"dark\") {\n\t\t\t\t\t\t\tdocument.documentElement.setAttribute(\"data-theme\", \"dark\");\n\t\t\t\t\t\t} else if (saved === \"light\") {\n\t\t\t\t\t\t\tdocument.documentElement.setAttribute(\"data-theme\", \"light\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdocument.documentElement.setAttribute(\"data-theme\", \"light\");\n\t\t\t\t\t}\n\t\t\t\t"}}]}],["$","body",null,{"children":["$","$L6",null,{"children":["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}]]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/a3a92ccd642ec2bc.css","precedence":"next","crossOrigin":""}],["$","link","1",{"rel":"stylesheet","href":"/_next/static/css/6f7575626860276e.css","precedence":"next","crossOrigin":""}]],"$L7"]]]] 8:I[2664,["250","static/chunks/250-9edeb9d2171db55c.js","839","static/chunks/839-022cf7a9ced654a8.js","877","static/chunks/app/%5B...slug%5D/page-0ab240b8cea332e0.js"],""] 9:I[1749,["250","static/chunks/250-9edeb9d2171db55c.js","839","static/chunks/839-022cf7a9ced654a8.js","877","static/chunks/app/%5B...slug%5D/page-0ab240b8cea332e0.js"],"Image"] 2:["$","div",null,{"className":"style_container__fKKke","children":[["$","$L8",null,{}],["$","div",null,{"className":"style_post__b12XM","children":[["$","div",null,{"className":"style_headerWords__W9tOe","id":"headerWords","children":["$","div",null,{"className":"style_container__kC8fE","children":[["$","div",null,{"className":"style_title__RVWQ2","children":"멋사 12기 중앙 해커톤 후기"}],["$","div",null,{"className":"style_description__dS1rF","children":"멋쟁이사자처럼 12기 중앙 해커톤에 참가했다."}]]}]}],["$","div",null,{"className":"style_time__vHpWr","children":"2024. 8. 7."}],["$","div",null,{"className":"style_title__mMA8e","children":"멋사 12기 중앙 해커톤 후기"}],["$","div",null,{"className":"style_description__XJTFe","children":"멋쟁이사자처럼 12기 중앙 해커톤에 참가했다."}],["$","div",null,{"className":"style_border__x5cYB","id":"headLine"}],["$","div",null,{"className":"style_contentStyle__Yta8A","children":[["$","div",null,{"className":"style_imageBox__6sQ34","children":[["$","$L9",null,{"src":"/project/web/likelion-hackathon/hackathon-1.jpg","alt":"/project/web/likelion-hackathon/hackathon-1.jpg","width":1000,"height":1000,"loading":"lazy","className":"style_img__fwG4y"}],["$","div",null,{"className":"style_description__GqC1z","children":""}]]}],"\n",[["$","h1",null,{"id":"목차","className":"style_heading__KYLdz style_heading1__0P2Vc","children":"목차"}],["$","div",null,{"className":"style_border__h4ej3"}]],"\n",["$","ul",null,{"className":"style_ul__SYxQ0","children":["\n",["$","li",null,{"className":"style_li__17u78","children":["$","a",null,{"href":"#%EB%82%B4-%EC%9D%B8%EC%83%9D-%EC%B2%AB-%ED%95%B4%EC%BB%A4%ED%86%A4","children":"내 인생 첫 해커톤","className":"style_a__MgrUe"}]}],"\n",["$","li",null,{"className":"style_li__17u78","children":[["$","a",null,{"href":"#%EB%B3%B8%EA%B2%A9%EC%A0%81%EC%9D%B8-%EA%B0%9C%EB%B0%9C","children":"본격적인 개발","className":"style_a__MgrUe"}],"\n",["$","ul",null,{"className":"style_ul__SYxQ0","children":["\n",["$","li",null,{"className":"style_li__17u78","children":["$","a",null,{"href":"#%EA%B9%83%ED%97%88%EB%B8%8C-repository-%EB%A7%81%ED%81%AC","children":"깃허브 Repository 링크","className":"style_a__MgrUe"}]}],"\n",["$","li",null,{"className":"style_li__17u78","children":["$","a",null,{"href":"#%EC%B1%84%ED%8C%85-%EC%84%9C%EB%B2%84-%EA%B5%AC%ED%98%84","children":"채팅 서버 구현","className":"style_a__MgrUe"}]}],"\n",["$","li",null,{"className":"style_li__17u78","children":["$","a",null,{"href":"#%EC%84%9C%EB%B2%84-%EB%B0%B0%ED%8F%AC","children":"서버 배포","className":"style_a__MgrUe"}]}],"\n",["$","li",null,{"className":"style_li__17u78","children":["$","a",null,{"href":"#%EC%B1%84%ED%8C%85-%ED%8E%98%EC%9D%B4%EC%A7%80-%EA%B5%AC%ED%98%84","children":"채팅 페이지 구현","className":"style_a__MgrUe"}]}],"\n"]}],"\n"]}],"\n",["$","li",null,{"className":"style_li__17u78","children":["$","a",null,{"href":"#%ED%95%B4%EC%BB%A4%ED%86%A4-%EB%8B%B9%EC%9D%BC","children":"해커톤 당일","className":"style_a__MgrUe"}]}],"\n",["$","li",null,{"className":"style_li__17u78","children":["$","a",null,{"href":"#%ED%9B%84%EA%B8%B0","children":"후기","className":"style_a__MgrUe"}]}],"\n"]}],"\n",[["$","h1",null,{"id":"내-인생-첫-해커톤","className":"style_heading__KYLdz style_heading1__0P2Vc","children":"내 인생 첫 해커톤"}],["$","div",null,{"className":"style_border__h4ej3"}]],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"드디어 멋쟁이사자처럼 중앙 해커톤의 막이 올랐다."}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"중앙 해커톤 참가는 내가 중앙대 멋사에 들어가게 된 이유 중 하나이기도 했다. 개발 협업 경험을 최대한 많이 쌓기 위해 들어간 멋사에서 해커톤 참가는 빼놓을 수 없기 때문이다. 지금까지 멋사에서 같이 활동했던 팀원들과 제한된 시간 안에 결과물을 만들어 낸다는 경험은 나에게 있어 정말 소중할 것이라고 생각해 이번 해커톤에서는 더욱 최선을 다해야겠다는 결심이 들었다."}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":["공지되었던 이번 해커톤의 주제는 ",["$","strong",null,{"className":"style_strong__ip7oe","children":"\"IT 기술을 활용하여 현대인의 건강 (wellness) 문제를 해결할 수 있는 서비스를 개발하시오.\""}]," 였다.",["$","br",null,{}],"\n","올해 초반에 각 대학에서 자체적으로 실시했던 멋사 아이디어톤 대회의 주제와 완전히 같아서 나를 포함한 대부분의 부원들이 당황했지만, 오히려 같은 주제로 인해 아이디어톤에서 접했던 시행착오들을 발판삼아 더 좋은 아이디어를 얻을 수 있을지도 모르기 때문에 단점보다 장점이 많을 것이라는 생각도 들었다. 그리고 해커톤인 만큼 ",["$","strong",null,{"className":"style_strong__ip7oe","children":"개발"}],"에 많은 비중을 둘 것이기 때문에 이전과 같은 경험이진 않을 것이라고 생각했다!"]}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"주제 발표 이후에는 기획 파트의 부원들이 각자가 구상한 이번 해커톤을 위한 서비스를 발표하고, 나머지 디자인, 프론트엔드, 백엔드 파트의 부원들이 마음에 드는 서비스를 골라 팀원으로 지원하는 팀 빌딩 과정을 거쳤다. 그렇게 정해진 팀에는 아는 얼굴들이 많아 매우 반가웠다! 😄"}],"\n",[["$","h1",null,{"id":"본격적인-개발","className":"style_heading__KYLdz style_heading1__0P2Vc","children":"본격적인 개발"}],["$","div",null,{"className":"style_border__h4ej3"}]],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":["멋쟁이사자처럼 중앙 해커톤은 약 한달간의 기간동안 주제에 알맞은 서비스를 개발하고 오프라인 해커톤 당일에 무박 2일로 개발 마무리 및 배포, 그리고 높은 점수를 받은 팀을 몇개 선정하여 발표를 진행하고 평가하는 형식으로 진행된다.",["$","br",null,{}],"\n","따라서 해커톤 당일 이전 한달동안 모든 개발을 끝내야 한다는 뜻이다. ",["$","del",null,{"children":"그러나 듣기로는 당일 마감 전까지 개발 안 한 팀이 단 한 팀도 없다고..."}]]}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"주어진 시간은 그렇게 많지 않다. 우리 팀도 팀이 편성되자마자 바로 서비스 개발에 착수했다."}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":["우리 팀이 만들고자 하는 서비스는 ",["$","strong",null,{"className":"style_strong__ip7oe","children":"\"운동 용품 대여로 현대인의 운동 부족을 해결해주는 서비스\""}]," 이다. 사용자간 물건을 주고받는 것은 당근마켓과 유사하지만 우리 서비스는 여러 물건들 중 운동 용품에 특화된 구조와 ",["$","strong",null,{"className":"style_strong__ip7oe","children":"대여"}],"를 지원한다는 특징을 차별점으로 설정했다."]}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":["그러므로 사용자들이 대여를 위해 서로 의사소통을 하기 위한 장치인 ",["$","strong",null,{"className":"style_strong__ip7oe","children":"채팅"}],"이 필수불가결하게 되었는데, 나는 이번에 이 채팅 시스템을 집중적으로 맡게 되었다."]}],"\n",[["$","h2",null,{"id":"깃허브-repository-링크","className":"style_heading__KYLdz style_heading2__HKlKs","children":"깃허브 Repository 링크"}],["$","div",null,{"className":"style_border__h4ej3"}]],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":["$","a",null,{"href":"https://github.com/Temple2001/likelion-hackathon-front","children":"프론트엔드 Repository","className":"style_a__MgrUe"}]}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":["$","a",null,{"href":"https://github.com/Temple2001/likelion-hackathon-back","children":"백엔드 Repository","className":"style_a__MgrUe"}]}],"\n",[["$","h2",null,{"id":"채팅-서버-구현","className":"style_heading__KYLdz style_heading2__HKlKs","children":"채팅 서버 구현"}],["$","div",null,{"className":"style_border__h4ej3"}]],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":["$","a",null,{"href":"https://velog.io/@mimijae/series/DRF%EB%A1%9C-%EC%9B%B9%EC%86%8C%EC%BC%93-%EC%B1%84%ED%8C%85-%EC%84%9C%EB%B2%84-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0","children":"DRF로 웹소켓 채팅 서버 구현하기 (by minjae_dev.log)","className":"style_a__MgrUe"}]}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"하지만 난 채팅을 구현해본 적이 단 한번도 없었고, 도대체 어떻게 구현해야 할지 막막하기만 할 뿐이었다. 그렇게 머리를 싸매던 도중 위와 같은 블로그 포스트를 보았고 약간의 희망을 되찾았다!"}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":["Django에는 WebSocket 통신 구현을 도와주는 ",["$","code",null,{"children":"Channels"}],"라는 라이브러리가 존재한다. 이를 통해 간단한 메서드 호출만으로 WebSocket 통신 관리를 수행할 수 있어 채팅 구현에 정말 많은 도움이 됐다."]}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"자세한 채팅 구현 과정은 아래의 포스트로 작성해 놓았다."}],"\n",["$","div",null,{"className":"style_blockquote__niUE0","children":[["$","div",null,{"className":"style_title__6ZTDk","children":[["$","svg",null,{"xmlns":"http://www.w3.org/2000/svg","width":100,"height":100,"viewBox":"0 0 32 32","className":"style_svg__QKgL7","children":["$","path",null,{"d":"M16 3C8.832 3 3 8.832 3 16s5.832 13 13 13 13-5.832 13-13S23.168 3 16 3Zm0 2c6.086 0 11 4.914 11 11s-4.914 11-11 11S5 22.086 5 16 9.914 5 16 5Zm-1 5v2h2v-2Zm0 4v8h2v-8Z"}]}],"Infomation"]}],["$","div",null,{"className":"style_content__Npgut","children":["\n",["$","div",null,{"className":"style_p__L5AWZ","children":"현재 작성중입니다."}],"\n"]}]]}],"\n",[["$","h2",null,{"id":"서버-배포","className":"style_heading__KYLdz style_heading2__HKlKs","children":"서버 배포"}],["$","div",null,{"className":"style_border__h4ej3"}]],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"이번 중앙 해커톤에서는 외부에서 URL로 접속 가능한 웹서비스를 만들어야 하기 때문에 서버 배포가 필수이다. WebSocket 채팅 기능이 포함되어 있기 때문에 서버 배포가 더 어려워지는 것 아닌가 하는 걱정이 이만저만이 아니었지만 채팅 구현 때 참고했던 위의 블로그에서 서버 배포까지 다루었기 때문에 마음이 편한 상태에서 개발에 집중할 수 있었다. (감사합니다... 😭)"}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"우리 팀이 만든 웹서비스는 Django 웹프레임워크, nginx 웹서버, postgreSQL 데이터베이스 서버, 그리고 Channels 라이브러리 구동에 필요한 Redis까지 총 4개의 프로그램이 동시에 동작해야 한다. 이걸 배포하고 수정할 때마다 하나씩 껐다 켰다 한다면 정말 정신이 나갈 것이므로, Docker를 이용해 컨테이너화 시키고 Docker Compose로 한번에 관리하여 개발 및 배포 프로세스를 간소화시켰다."}],"\n",["$","div",null,{"data-rehype-pretty-code-fragment":"","children":[["$","div",null,{"data-rehype-pretty-code-title":"","data-language":"yml","data-theme":"default","children":"docker-compose.yml"}],["$","pre",null,{"className":"dark-plus","style":{"backgroundColor":"#1E1E1E"},"tabIndex":"0","data-language":"yml","data-theme":"default","children":["$","code",null,{"data-language":"yml","data-theme":"default","style":{"display":"grid"},"children":[["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#569CD6"},"children":"services"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"nginx"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"build"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":": "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"./nginx"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"image"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":": "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"wooga_nginx:latest"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"volumes"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" - "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"static_volume:/usr/src/app/_static"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" - "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"$${ssl 키 경로}:/etc/nginx/ssl/fullchain.pem:ro"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" - "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"$${ssl 키 경로}:/etc/nginx/ssl/privkey.pem:ro"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"ports"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" - "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"80:80"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" - "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"443:443"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"depends_on"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" - "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"web"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"env_file"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" - "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"./.env.prod"}]]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"web"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"build"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":": "}],["$","span",null,{"style":{"color":"#B5CEA8"},"children":"."}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"image"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":": "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"wooga_web:latest"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"command"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":": "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"daphne likelion_hackathon.asgi:application --port 8000 --bind 0.0.0.0 -v2"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"volumes"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" - "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"static_volume:/usr/src/app/_static"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" - "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"./:/usr/src/app/"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"expose"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" - "}],["$","span",null,{"style":{"color":"#B5CEA8"},"children":"8000"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"env_file"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" - "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"./.env.prod"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"depends_on"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" - "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"db"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" - "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"redis"}]]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"db"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"image"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":": "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"postgres:12.0-alpine"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"volumes"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" - "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"postgres_data:/var/lib/postgresql/data/"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"env_file"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" - "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"./.env.prod"}]]}],"\n",["$","span",null,{"data-line":"","children":" "}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"redis"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"image"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":": "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"redis:alpine"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"ports"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" - "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"\"6379:6379\""}]]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#569CD6"},"children":"volumes"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"postgres_data"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"static_volume"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}]]}]}]]}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"Django를 일반적인 REST API를 제공하는 서비스로 사용할 때는 Python 웹 어플리케이션과 웹 서버 사이에 WSGI(Web Server Gateway Interface)를 사용한다. WSGI는 동기식 인터페이스로 하나의 요청이 완전히 처리될 때까지 다른 요청을 기다리게 한다. 그러나 이번 개발에서 사용될 WebSocket 통신은 동기식 인터페이스로는 제대로 작동할 수 없다. 따라서 ASGI(Asynchronous Server Gateway Interface)를 사용해 비동기식 프로그래밍을 구현해야 한다. Django는 기본적으로 ASGI가 아닌 WSGI를 사용하므로, 별도의 설정이 필요하다."}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":["Django를 배포할 때 일반적으로 사용하는 gunicorn은 WSGI 서버이므로, ASGI 서버를 사용해야 한다. 이번 프로젝트에서 사용된 ASGI 서버는 ",["$","strong",null,{"className":"style_strong__ip7oe","children":"Daphne(다프네)"}]," 이다. 위 ",["$","code",null,{"children":"docker-compose.yml"}]," 코드에서도 Django를 배포할 때 daphne가 사용된 것을 볼 수 있다."]}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"DB로 PostgreSQL을 사용하지만 추가적으로 Redis를 사용하는 이유는 Django Channels가 메시지 브로커의 기능을 위해 Redis를 사용하기 때문이다. 채널 레이어에서의 데이터 전달을 위한 메커니즘을 제공하며, 이를 통해 다수의 컨슈머가 서로 통신할 수 있도록 해준다고 한다. Channels 공식문서에서도 Redis 사용을 적극 권장하고 있다."}],"\n",["$","hr",null,{"className":"style_hr__va5an"}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"그리고 Docker Compose를 이용한 배포에 더해 Github Actions를 이용한 자동 배포도 구성했다."}],"\n",["$","div",null,{"data-rehype-pretty-code-fragment":"","children":[["$","div",null,{"data-rehype-pretty-code-title":"","data-language":"yml","data-theme":"default","children":"auto-deploy.yml"}],["$","pre",null,{"className":"dark-plus","style":{"backgroundColor":"#1E1E1E"},"tabIndex":"0","data-language":"yml","data-theme":"default","children":["$","code",null,{"data-language":"yml","data-theme":"default","style":{"display":"grid"},"children":[["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#569CD6"},"children":"name"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":": "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"Auto Deploy with Docker Compose"}]]}],"\n",["$","span",null,{"data-line":"","children":" "}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#569CD6"},"children":"on"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"push"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"branches"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" - "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"dev"}]]}],"\n",["$","span",null,{"data-line":"","children":" "}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#569CD6"},"children":"jobs"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"deploy"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"runs-on"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":": "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"ubuntu-latest"}]]}],"\n",["$","span",null,{"data-line":"","children":" "}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"steps"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" - "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"name"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":": "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"Execute remote SSH commands"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"uses"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":": "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"appleboy/ssh-action@v1.0.3"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"with"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":":"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"host"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":": "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"$${{ secrets.SSH_HOST }}"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"username"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":": "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"$${{ secrets.SSH_USER }}"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"key"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":": "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"$${{ secrets.SSH_KEY }}"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"port"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":": "}],["$","span",null,{"style":{"color":"#CE9178"},"children":"$${{ secrets.SSH_PORT }}"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"script"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":": "}],["$","span",null,{"style":{"color":"#C586C0"},"children":"|"}]]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#CE9178"},"children":" cd /home/ubuntu/repositories/woogaback"}]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#CE9178"},"children":" docker compose down"}]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#CE9178"},"children":" git pull"}]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#CE9178"},"children":" docker compose up -d"}]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#CE9178"},"children":" docker compose exec web python manage.py makemigrations"}]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#CE9178"},"children":" docker compose exec web python manage.py migrate"}]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#CE9178"},"children":" docker image prune -f"}]}]]}]}]]}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":["이미 Docker Compose로 배포 과정을 간소화하였기 때문에 Github Actions에서 수행해야 할 작업은 그저 ",["$","code",null,{"children":"git pull"}]," 이후에 ",["$","code",null,{"children":"docker compose up -d"}]," 뿐이다. 안정적인 배포를 위해 Django migration 작업과 docker 캐시 삭제 작업을 추가했다."]}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":["이로써 ",["$","code",null,{"children":"dev"}]," 브랜치에 push하는 작업만으로 자동으로 서버가 변경사항을 반영하여 배포까지 수행하는 환경을 구성하게 되었다!"]}],"\n",[["$","h2",null,{"id":"채팅-페이지-구현","className":"style_heading__KYLdz style_heading2__HKlKs","children":"채팅 페이지 구현"}],["$","div",null,{"className":"style_border__h4ej3"}]],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":["이렇게 이번 프로젝트에서 나의 역할은 끝나......는 줄 알았으나 프론트 파트 팀원들이 엄청난 양의 작업으로 고통스러워 하던 상황이라 어찌저찌 내가 채팅 웹 페이지까지 구현하게 되었다.",["$","br",null,{}],"\n",["$","div",null,{"className":"style_imageBox__6sQ34","children":[["$","$L9",null,{"src":"https://findthatmeme.us-southeast-1.linodeobjects.com/aee2d1bc-b564-40a0-8466-cf3e9e21f565.jpg","alt":"https://findthatmeme.us-southeast-1.linodeobjects.com/aee2d1bc-b564-40a0-8466-cf3e9e21f565.jpg","width":1000,"height":1000,"loading":"lazy","className":"style_img__fwG4y"}],["$","div",null,{"className":"style_description__GqC1z","children":"살려줘"}]]}],["$","br",null,{}],"\n","오랜만에 하는 프론트엔드 개발이라 고된 행군길이 예상되었지만 오히려 채팅 서버를 직접 구현했었기 때문인지 그렇게 막막한 작업은 아니었다."]}],"\n",["$","div",null,{"data-rehype-pretty-code-fragment":"","children":[["$","div",null,{"data-rehype-pretty-code-title":"","data-language":"js","data-theme":"default","children":"websocket.js"}],["$","pre",null,{"className":"dark-plus","style":{"backgroundColor":"#1E1E1E"},"tabIndex":"0","data-language":"js","data-theme":"default","children":["$","code",null,{"data-language":"js","data-theme":"default","style":{"display":"grid"},"children":[["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#D4D4D4"},"children":"..."}]}],"\n",["$","span",null,{"data-line":"","children":" "}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#569CD6"},"children":"class"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#4EC9B0"},"children":"WebSocketService"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":" {"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t"}],["$","span",null,{"style":{"color":"#569CD6"},"children":"constructor"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"() {"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t\t"}],["$","span",null,{"style":{"color":"#569CD6"},"children":"this"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"."}],["$","span",null,{"style":{"color":"#9CDCFE"},"children":"socket"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":" = "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"null"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":";"}]]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t}"}]}],"\n",["$","span",null,{"data-line":"","children":" "}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t"}],["$","span",null,{"style":{"color":"#6A9955"},"children":"/**"}]]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#6A9955"},"children":"\t * WebSocket 연결을 설정하는 함수"}]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#6A9955"},"children":"\t */"}]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t"}],["$","span",null,{"style":{"color":"#DCDCAA"},"children":"connect"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"("}],["$","span",null,{"style":{"color":"#9CDCFE"},"children":"roomId"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":", "}],["$","span",null,{"style":{"color":"#9CDCFE"},"children":"onMessage"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":") {"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t\t"}],["$","span",null,{"style":{"color":"#569CD6"},"children":"this"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"."}],["$","span",null,{"style":{"color":"#9CDCFE"},"children":"socket"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":" = "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"new"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#DCDCAA"},"children":"WebSocket"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"("}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t\t\t"}],["$","span",null,{"style":{"color":"#CE9178"},"children":"`wss://"}],["$","span",null,{"style":{"color":"#569CD6"},"children":"$${"}],["$","span",null,{"style":{"color":"#4FC1FF"},"children":"SERVER_URL"}],["$","span",null,{"style":{"color":"#569CD6"},"children":"}"}],["$","span",null,{"style":{"color":"#CE9178"},"children":"/ws/room/"}],["$","span",null,{"style":{"color":"#569CD6"},"children":"$${"}],["$","span",null,{"style":{"color":"#9CDCFE"},"children":"roomId"}],["$","span",null,{"style":{"color":"#569CD6"},"children":"}"}],["$","span",null,{"style":{"color":"#CE9178"},"children":"/messages`"}]]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t\t);"}]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t\t"}],["$","span",null,{"style":{"color":"#569CD6"},"children":"this"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"."}],["$","span",null,{"style":{"color":"#9CDCFE"},"children":"socket"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"."}],["$","span",null,{"style":{"color":"#DCDCAA"},"children":"onmessage"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":" = ("}],["$","span",null,{"style":{"color":"#9CDCFE"},"children":"event"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":") "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"=>"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":" {"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t\t\t"}],["$","span",null,{"style":{"color":"#569CD6"},"children":"const"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#4FC1FF"},"children":"data"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":" = "}],["$","span",null,{"style":{"color":"#4FC1FF"},"children":"JSON"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"."}],["$","span",null,{"style":{"color":"#DCDCAA"},"children":"parse"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"("}],["$","span",null,{"style":{"color":"#9CDCFE"},"children":"event"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"."}],["$","span",null,{"style":{"color":"#9CDCFE"},"children":"data"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":");"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t\t\t"}],["$","span",null,{"style":{"color":"#DCDCAA"},"children":"onMessage"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"("}],["$","span",null,{"style":{"color":"#9CDCFE"},"children":"data"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":");"}]]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t\t};"}]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t}"}]}],"\n",["$","span",null,{"data-line":"","children":" "}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t"}],["$","span",null,{"style":{"color":"#6A9955"},"children":"/**"}]]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#6A9955"},"children":"\t * WebSocket을 통해 메시지를 전송하는 함수"}]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#6A9955"},"children":"\t */"}]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t"}],["$","span",null,{"style":{"color":"#DCDCAA"},"children":"sendMessage"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"("}],["$","span",null,{"style":{"color":"#9CDCFE"},"children":"message"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":") {"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t\t"}],["$","span",null,{"style":{"color":"#C586C0"},"children":"if"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":" ("}],["$","span",null,{"style":{"color":"#569CD6"},"children":"this"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"."}],["$","span",null,{"style":{"color":"#9CDCFE"},"children":"socket"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":" && "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"this"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"."}],["$","span",null,{"style":{"color":"#9CDCFE"},"children":"socket"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"."}],["$","span",null,{"style":{"color":"#9CDCFE"},"children":"readyState"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":" === "}],["$","span",null,{"style":{"color":"#9CDCFE"},"children":"WebSocket"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"."}],["$","span",null,{"style":{"color":"#4FC1FF"},"children":"OPEN"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":") {"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t\t\t"}],["$","span",null,{"style":{"color":"#569CD6"},"children":"this"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"."}],["$","span",null,{"style":{"color":"#9CDCFE"},"children":"socket"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"."}],["$","span",null,{"style":{"color":"#DCDCAA"},"children":"send"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"("}],["$","span",null,{"style":{"color":"#4FC1FF"},"children":"JSON"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"."}],["$","span",null,{"style":{"color":"#DCDCAA"},"children":"stringify"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"("}],["$","span",null,{"style":{"color":"#9CDCFE"},"children":"message"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"));"}]]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t\t}"}]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t}"}]}],"\n",["$","span",null,{"data-line":"","children":" "}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t"}],["$","span",null,{"style":{"color":"#6A9955"},"children":"/**"}]]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#6A9955"},"children":"\t * WebSocket 연결을 해제하는 함수"}]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#6A9955"},"children":"\t */"}]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t"}],["$","span",null,{"style":{"color":"#DCDCAA"},"children":"disconnect"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"() {"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t\t"}],["$","span",null,{"style":{"color":"#C586C0"},"children":"if"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":" ("}],["$","span",null,{"style":{"color":"#569CD6"},"children":"this"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"."}],["$","span",null,{"style":{"color":"#9CDCFE"},"children":"socket"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":") {"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t\t\t"}],["$","span",null,{"style":{"color":"#569CD6"},"children":"this"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"."}],["$","span",null,{"style":{"color":"#9CDCFE"},"children":"socket"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"."}],["$","span",null,{"style":{"color":"#DCDCAA"},"children":"close"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"();"}]]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t\t}"}]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#D4D4D4"},"children":"\t}"}]}],"\n",["$","span",null,{"data-line":"","children":["$","span",null,{"style":{"color":"#D4D4D4"},"children":"}"}]}],"\n",["$","span",null,{"data-line":"","children":" "}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#569CD6"},"children":"const"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#4FC1FF"},"children":"webSocketService"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":" = "}],["$","span",null,{"style":{"color":"#569CD6"},"children":"new"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#DCDCAA"},"children":"WebSocketService"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":"();"}]]}],"\n",["$","span",null,{"data-line":"","children":[["$","span",null,{"style":{"color":"#C586C0"},"children":"export"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#C586C0"},"children":"default"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":" "}],["$","span",null,{"style":{"color":"#9CDCFE"},"children":"webSocketService"}],["$","span",null,{"style":{"color":"#D4D4D4"},"children":";"}]]}]]}]}]]}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"채팅 페이지 개발에 가장 중점이 되는 부분은 역시 채팅을 위한 WebSocket 통신 부분이었다. 그러나 javascript 자체에서 WebSocket 통신을 위한 기능을 지원해주기도 하고, 레퍼런스들도 인터넷에 많이 올라와 있어 개발하는데 큰 문제를 겪지 않았다."}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":["웹소켓 통신을 하는 법은 간단하다. 채팅 API 주소로 WebSocket 클래스의 인스턴스를 생성하고, 그 인스턴스의 ",["$","code",null,{"children":"onmessage"}],"에 메시지를 받았을 때 수행할 함수를 할당하고 ",["$","code",null,{"children":"send"}]," 메서드로 메시지를 전송하면 된다. 그 과정에서의 네트워크 관리는 모두 javascript 내부에서 담당한다."]}],"\n",["$","div",null,{"className":"style_imageBox__6sQ34","children":[["$","$L9",null,{"src":"/project/web/likelion-hackathon/hackathon-2.gif","alt":"/project/web/likelion-hackathon/hackathon-2.gif","width":1000,"height":1000,"loading":"lazy","className":"style_img__fwG4y"}],["$","div",null,{"className":"style_description__GqC1z","children":"작동이 아주 잘 된다"}]]}],"\n",[["$","h1",null,{"id":"해커톤-당일","className":"style_heading__KYLdz style_heading1__0P2Vc","children":"해커톤 당일"}],["$","div",null,{"className":"style_border__h4ej3"}]],"\n",["$","div",null,{"className":"style_imageBox__6sQ34","children":[["$","$L9",null,{"src":"/project/web/likelion-hackathon/hackathon-3.jpg","alt":"/project/web/likelion-hackathon/hackathon-3.jpg","width":1000,"height":1000,"loading":"lazy","className":"style_img__fwG4y"}],["$","div",null,{"className":"style_description__GqC1z","children":"북적북적"}]]}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"역시나 우려대로 해커톤 전날까지 개발을 마치는 것은 무리였고, 우리 팀은 어떻게든 필수적인 기능들만 마무리하고 해커톤 당일에 오프라인에서 개발을 마치기로 했다."}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"백엔드 API는 모두 준비가 된 상태였고, 문제는 수많은 프론트엔드 페이지의 잡다한 버그들이었다. 나는 일단 백엔드 파트이고, 채팅 페이지를 직접 구현하긴 했지만 그 이외의 프론트 프로젝트 구조에 대한 정보는 전혀 없던 상태였다. 그러나 프론트엔드 팀원들이 고통받고 있는 모습을 본체만체할 수는 없었기 때문에 어떻게든 빠른 시간 내에 코드를 이해하고 버그를 잡으려 노력했다."}],"\n",["$","div",null,{"className":"style_imageBox__6sQ34","children":[["$","$L9",null,{"src":"/project/web/likelion-hackathon/hackathon-4.png","alt":"/project/web/likelion-hackathon/hackathon-4.png","width":1000,"height":1000,"loading":"lazy","className":"style_img__fwG4y"}],["$","div",null,{"className":"style_description__GqC1z","children":"7시간동안의 처절한 사투..."}]]}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"다만 다행이었던 점은 백엔드 API에서는 치명적인 버그나 잘못 설계된 부분이 없었다는 점이었고, 이런저런 수정사항이 있긴 있었지만 이전에 구성했던 자동 배포 환경 덕분에 매우 빠르게 수정사항을 반영할 수 있었다. 고생했던 노력이 빛을 발하는 순간이었다."}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"제출 전 마지막 5분은 정말 피말리는 상황이 벌어졌는데, 깜빡하고 중간에 바뀐 서비스 로고가 반영되지 않았다는 사실을 깨달은 것이었다. 밤 12시 정각 이후에 깃허브 레포에 커밋 내역이 있으면 탈락 처리되므로 무조건 12시 이전에 수정해야 했었는데, 모두가 힘을 합쳐 제출하기 약 30초전에 아슬아슬하게 커밋에 성공했다! 끝까지 안심할 수 없었던 해커톤이었다..."}],"\n",["$","div",null,{"className":"style_imageBox__6sQ34","children":[["$","$L9",null,{"src":"/project/web/likelion-hackathon/hackathon-5.jpg","alt":"/project/web/likelion-hackathon/hackathon-5.jpg","width":1000,"height":1000,"loading":"lazy","className":"style_img__fwG4y"}],["$","div",null,{"className":"style_description__GqC1z","children":"짠"}]]}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"제출이 끝나고 긴장이 풀리고 나니 온 몸에 힘이 다 빠진 상태였지만 그래도 끝내 프로젝트를 완성시켰다는 성취감이 정말 기분 좋았다. 고생했던 팀원들과 함께 배달시킨 음식을 먹으며 해커톤 중에 있었던 이야기들을 서로 나누니 쌓였던 피로감도 날아가는 기분이었다!!"}],"\n",["$","div",null,{"className":"style_imageBox__6sQ34","children":[["$","$L9",null,{"src":"/project/web/likelion-hackathon/hackathon-7.jpg","alt":"/project/web/likelion-hackathon/hackathon-7.jpg","width":1000,"height":1000,"loading":"lazy","className":"style_img__fwG4y"}],["$","div",null,{"className":"style_description__GqC1z","children":"결국 날을 샜구나"}]]}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"그렇게 밤새도록 우리 팀이랑 중앙대학교 멋사 부원들과 서로 만든 서비스를 구경하거나 수다 떨며 노는 등 결과 발표 전까지 시간을 보냈고 발표가 끝나자마자 피곤한 몸을 이끌고 집으로 돌아갔다. 집에 돌아가는 과정은 피곤해서 기억조차 안날 정도로 힘들었던 것 같다..."}],"\n",[["$","h1",null,{"id":"후기","className":"style_heading__KYLdz style_heading1__0P2Vc","children":"후기"}],["$","div",null,{"className":"style_border__h4ej3"}]],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"처음으로 참가했던 해커톤인 멋쟁이사자처럼 중앙 해커톤은 성공적이었다."}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"비록 수상을 하진 못했지만 개발 협업 경험을 최대한 쌓고 싶다는 내 목적은 원없이 달성할 수 있었고, 서버 관리 및 배포 경험과 언젠가 시도해보고 싶었던 웹소켓 통신 경험을 쌓을 수 있어서 여러모로 많은 것을 얻고 갈 수 있었다고 생각한다. 특히 채팅 기능은 백엔드와 프론트엔드 모두 직접 개발했다 보니 웹소켓 통신 과정을 더욱 잘 이해할 수 있었다. 그리고 이번에는 docker compose를 이용해 서버 배포 및 자동화를 구성하였는데, 다음 프로젝트에는 k8s를 이용해 더 체계적이고 색다른 배포 경험을 해보고 싶다는 생각도 들었다."}],"\n",["$","div",null,{"className":"style_p__L5AWZ","children":"첫 술에 배부를 수 없다라는 말처럼 처음부터 눈에 띄는 결과를 얻을 수 있을 거라고는 애초에 생각하지도 않았다. 대신 이번 과정을 통해 얻은 경험은 오랫동안 남을 수 있는 귀중한 자원이므로 이런 경험들을 최대한 살려 내 강점으로 만들 수 있도록 하는 것이 가장 중요하다고 생각한다."}],"\n",["$","div",null,{"className":"style_imageBox__6sQ34","children":[["$","$L9",null,{"src":"/project/web/likelion-hackathon/hackathon-6.jpg","alt":"/project/web/likelion-hackathon/hackathon-6.jpg","width":1000,"height":1000,"loading":"lazy","className":"style_img__fwG4y"}],["$","div",null,{"className":"style_description__GqC1z","children":"모두 고생 많았어!!!"}]]}]]}]]}]]}] 7:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"멋사 12기 중앙 해커톤 후기 | Temple's Hideout"}],["$","meta","3",{"name":"description","content":"멋쟁이사자처럼 12기 중앙 해커톤에 참가했다."}],["$","meta","4",{"property":"og:title","content":"멋사 12기 중앙 해커톤 후기 | Temple's Hideout"}],["$","meta","5",{"property":"og:description","content":"멋쟁이사자처럼 12기 중앙 해커톤에 참가했다."}],["$","meta","6",{"property":"og:image","content":"https://blog.templ.es/opengraph/project/web/likelion-hackathon"}],["$","meta","7",{"name":"twitter:card","content":"summary_large_image"}],["$","meta","8",{"name":"twitter:title","content":"멋사 12기 중앙 해커톤 후기 | Temple's Hideout"}],["$","meta","9",{"name":"twitter:description","content":"멋쟁이사자처럼 12기 중앙 해커톤에 참가했다."}],["$","meta","10",{"name":"twitter:image","content":"https://blog.templ.es/opengraph/project/web/likelion-hackathon"}],["$","link","11",{"rel":"icon","href":"/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","meta","12",{"name":"next-size-adjust"}]] 1:null