diff --git a/contentlayer.config.ts b/contentlayer.config.ts index 496dafe..6e2a076 100644 --- a/contentlayer.config.ts +++ b/contentlayer.config.ts @@ -28,14 +28,15 @@ export const Post = defineDocumentType(() => ({ default: false, }, }, - /* computedFields: { - publicPath: { + previewImage: { type: "string", - resolve: (post) => "/" + post._raw.flattenedPath, + resolve: (post) => { + const match = post.body.raw.match(/!\[.*?\]\((.*?)\)/); + return match ? match[1] : ""; + }, }, }, - */ })); export const Category = defineDocumentType(() => ({ diff --git a/public/project/python/likelion-hackathon/_post.mdx b/public/project/python/likelion-hackathon/_post.mdx deleted file mode 100644 index 5a6b6ce..0000000 --- a/public/project/python/likelion-hackathon/_post.mdx +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: 멋사 12기 중앙 해커톤 후기 -description: 멋쟁이사자처럼 12기 중앙 해커톤에 참가했다. -createdAt: 2024-08-07 -blind: true ---- - -> 현재 이 글은 작성중입니다. - -# 목차 - -# 준비 diff --git a/public/project/web/likelion-hackathon/_post.mdx b/public/project/web/likelion-hackathon/_post.mdx new file mode 100644 index 0000000..229d98b --- /dev/null +++ b/public/project/web/likelion-hackathon/_post.mdx @@ -0,0 +1,52 @@ +--- +title: 멋사 12기 중앙 해커톤 후기 +description: 멋쟁이사자처럼 12기 중앙 해커톤에 참가했다. +createdAt: 2024-08-07 +blind: true +--- + +> 현재 이 글은 작성중입니다. + +# 목차 + +# 내 인생 첫 해커톤 +드디어 멋쟁이사자처럼 중앙 해커톤의 막이 올랐다. + +중앙 해커톤 참가는 내가 중앙대 멋사에 들어가게 된 이유 중 하나이기도 했다. 개발 협업 경험을 최대한 많이 쌓기 위해 들어간 멋사에서 해커톤 참가는 빼놓을 수 없기 때문이다. 지금까지 멋사에서 같이 활동했던 팀원들과 제한된 시간 안에 결과물을 만들어 낸다는 경험은 나에게 있어 정말 소중할 것이라고 생각해 이번 해커톤에서는 더욱 최선을 다해야겠다는 결심이 들었다. + +공지되었던 이번 해커톤의 주제는 **"IT 기술을 활용하여 현대인의 건강 (wellness) 문제를 해결할 수 있는 서비스를 개발하시오."** 였다. +올해 초반에 각 대학에서 자체적으로 실시했던 멋사 아이디어톤 대회의 주제와 완전히 같아서 나를 포함한 대부분의 부원들이 당황했지만, 오히려 같은 주제로 인해 아이디어톤에서 접했던 시행착오들을 발판삼아 더 좋은 아이디어를 얻을 수 있을지도 모르기 때문에 단점보다 장점이 많을 것이라는 생각도 들었다. 그리고 해커톤인 만큼 **개발**에 많은 비중을 둘 것이기 때문에 이전과 같은 경험이진 않을 것이라고 생각했다! + +주제 발표 이후에는 기획 파트의 부원들이 각자가 구상한 이번 해커톤을 위한 서비스를 발표하고, 나머지 디자인, 프론트엔드, 백엔드 파트의 부원들이 마음에 드는 서비스를 골라 팀원으로 지원하는 팀 빌딩 과정을 거쳤다. 그렇게 정해진 팀에는 아는 얼굴들이 많아 매우 반가웠다! 😄 + +# 본격적인 개발 +멋쟁이사자처럼 중앙 해커톤은 약 한달간의 기간동안 주제에 알맞은 서비스를 개발하고 오프라인 해커톤 당일에 무박 2일로 개발 마무리 및 배포, 그리고 높은 점수를 받은 팀을 몇개 선정하여 발표를 진행하고 평가하는 형식으로 진행된다. +따라서 해커톤 당일 이전 한달동안 모든 개발을 끝내야 한다는 뜻이다. ~~그러나 듣기로는 당일 마감 전까지 개발 안 한 팀이 단 한 팀도 없다고...~~ + +주어진 시간은 그렇게 많지 않다. 우리 팀도 팀이 편성되자마자 바로 서비스 개발에 착수했다. + +우리 팀이 만들고자 하는 서비스는 **"운동 용품 대여로 현대인의 운동 부족을 해결해주는 서비스"** 이다. 사용자간 물건을 주고받는 것은 당근마켓과 유사하지만 우리 서비스는 여러 물건들 중 운동 용품에 특화된 구조와 **대여**를 지원한다는 특징을 차별점으로 설정했다. + +그러므로 사용자들이 대여를 위해 서로 의사소통을 하기 위한 장치인 **채팅**이 필수불가결하게 되었는데, 나는 이번에 이 채팅 시스템을 집중적으로 맡게 되었다. + +## 깃허브 Repository 링크 + +[프론트엔드 Repository](https://github.com/Temple2001/likelion-hackathon-front) + +[백엔드 Repository](https://github.com/Temple2001/likelion-hackathon-back) + +## 채팅 구현 +[DRF로 웹소켓 채팅 서버 구현하기](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) + +하지만 난 채팅을 구현해본 적이 단 한번도 없었고, 도대체 어떻게 구현해야 할지 막막하기만 할 뿐이었다. 그렇게 머리를 싸매던 도중 위와 같은 블로그 포스트를 보았고 약간의 희망을 되찾았다! + +Django에는 웹소켓 통신 구현을 도와주는 `Channels`라는 라이브러리가 존재한다. 이를 통해 간단한 메서드 호출만으로 웹소켓 통신 관리를 수행할 수 있어 채팅 구현에 정말 많은 도움이 됐다. + +자세한 채팅 구현 과정은 아래의 포스트로 작성해 놓았다. + +> 현재 작성중입니다. + +## 서버 배포 +이번 중앙 해커톤에서는 외부에서 URL로 접속 가능한 웹서비스를 만들어야 하기 때문에 서버 배포가 필수이다. 웹소켓 채팅 기능이 포함되어 있기 때문에 서버 배포가 더 어려워지는 것 아닌가 하는 걱정이 이만저만이 아니었지만 채팅 구현 때 참고했던 위의 블로그에서 서버 배포까지 다루었기 때문에 마음이 편한 상태에서 개발에 집중할 수 있었다. (감사합니다... 😭) + +우리 팀이 만든 웹서비스는 Django 웹프레임워크, nginx 웹서버, postgreSQL 데이터베이스 서버, 그리고 Channels 라이브러리 구동에 필요한 Redis까지 총 4개의 프로그램이 동시에 동작해야 한다. 이걸 배포하고 수정할 때마다 하나씩 껐다 켰다 한다면 정말 정신이 나갈 것이므로, Docker를 이용해 컨테이너화 시키고 Docker Compose로 한번에 관리하여 개발 및 배포 프로세스를 간소화시켰다. \ No newline at end of file diff --git a/src/app/[...slug]/page.tsx b/src/app/[...slug]/page.tsx index 3e2259b..a5f23e2 100644 --- a/src/app/[...slug]/page.tsx +++ b/src/app/[...slug]/page.tsx @@ -65,12 +65,14 @@ export default async function ({ params }: { params: { slug: string[] } }) { export async function generateStaticParams() { const docs = allDocuments; - return docs.map((doc) => { - const path = doc._raw.sourceFileDir; - return { - slug: path.split("/"), - }; - }); + return docs + .filter((doc) => !/\.(png|jpg|jpeg|gif)$/.test(doc._raw.sourceFileName)) + .map((doc) => { + const path = doc._raw.sourceFileDir; + return { + slug: path.split("/"), + }; + }); } export function generateMetadata({ params }: { params: { slug: string[] } }) { diff --git a/src/components/MDXComponents/style.module.scss b/src/components/MDXComponents/style.module.scss index 4c65b93..40bd4d1 100644 --- a/src/components/MDXComponents/style.module.scss +++ b/src/components/MDXComponents/style.module.scss @@ -62,8 +62,9 @@ .p { font-size: 1.1rem; + font-weight: 300; line-height: 170%; - margin: 0.5rem 0; + margin: 1rem 0; @media screen and (max-width: 1000px) { font-size: 1rem; diff --git a/src/components/layout/footer/index.tsx b/src/components/layout/footer/index.tsx index 5b75a28..f963fbb 100644 --- a/src/components/layout/footer/index.tsx +++ b/src/components/layout/footer/index.tsx @@ -16,7 +16,7 @@ export default function Footer() { {blogdata.blogName}
- © 2023 DongYoung Kim. All rights reserved. + © 2024 DongYoung Kim. All rights reserved.
Thank you for visiting my blog. Your interest gives me a great diff --git a/src/components/layout/postbox/index.tsx b/src/components/layout/postbox/index.tsx index dac467b..9529281 100644 --- a/src/components/layout/postbox/index.tsx +++ b/src/components/layout/postbox/index.tsx @@ -3,15 +3,32 @@ import styles from "./style.module.scss"; import { Post } from "@/contentlayer/generated"; export default function PostBox({ post }: { post: Post }) { + const convertLink = (post: Post) => { + if ( + post.previewImage.startsWith("http://") || + post.previewImage.startsWith("https://") + ) { + return post.previewImage; + } else { + return `/${post._raw.sourceFileDir}/${post.previewImage}`; + } + }; return (
-
{post.title}
-
{post.description}
-
- {new Date(post.createdAt).toLocaleDateString("ko-KR")} +
+
{post.title}
+
{post.description}
+
+ {new Date(post.createdAt).toLocaleDateString("ko-KR")} +
+ {post.previewImage && ( +
+ preview image +
+ )}
diff --git a/src/components/layout/postbox/style.module.scss b/src/components/layout/postbox/style.module.scss index a0ae27d..7d1011f 100644 --- a/src/components/layout/postbox/style.module.scss +++ b/src/components/layout/postbox/style.module.scss @@ -19,36 +19,62 @@ } */ .postBox { - padding: 1rem 1.5rem; - - .title { - font-size: 1.8rem; - font-weight: 600; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - margin-bottom: 0.5rem; - @media screen and (max-width: 1000px) { - font-size: 1.6rem; + display: flex; + height: 10rem; + + .textBox { + width: 60%; + flex: 6; + padding: 1rem 1.5rem; + display: flex; + flex-direction: column; + + .title { + font-size: 1.8rem; + font-weight: 600; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-bottom: 0.5rem; + @media screen and (max-width: 1000px) { + font-size: 1.6rem; + } } - } - .description { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - margin-bottom: 0.5rem; - @media screen and (max-width: 1000px) { + .description { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-bottom: 0.5rem; + @media screen and (max-width: 1000px) { + font-size: 0.9rem; + } + } + + .time { font-size: 0.9rem; + font-style: italic; + font-weight: 400; + text-align: left; + opacity: 0.7; + margin-top: auto; } } - .time { - font-size: 0.9rem; - font-style: italic; - font-weight: 400; - text-align: right; - opacity: 0.7; + .imageBox { + @media screen and (max-width: 1000px) { + display: none; + } + + flex: 4; + flex-shrink: 0; + + img { + width: 100%; + height: 100%; + object-fit: cover; + clip-path: polygon(15% 0, 100% 0, 100% 100%, 0 100%); + } } } }