✔️ 프론트엔드
- (프레임워크) NEXTJS 13
- (상태관리) React Hooks, React ContextAPI
- (서버상태관리) useSWR
- (스타일) tailwindCSS, Chakra-ui
✔️ 백엔드 API 및 백엔드 서버 호스팅
- (백엔드 API) Supabase Edge Functions (like awsLambda)
- (백엔드 서버 호스팅) Supabase
✔️ 클라이언트 호스팅
- (클라이언트 호스팅) AWS S3, AWS Route53, AWS Cloudfront
- (도메인 구매) Gabia
저번 소프트웨어 마에스트로 프로젝트로 제 기간에 끝내지 못했던 이유는 MVP
를 잘못 잡은 것에 있다고 생각하여 개인 프로젝트로 한 잡봇
은 최소 기능만을 가진 서비스로 빠르게 만들어서 조금이라도 사람들이 빠르게 사용 할 수 있었으면 좋겠다 라고 생각했다.
하지만, MVP로 개발 하였지만 남에게 보여준다는 생각을 한다는 것 자체가 어느정도 부담이 있었고, 이 때문에 계속해서 기능들을 만들고, UI와 UX를 변경하는 등 시간을 계속해서 사용 했다.
물론, 이 서비스를 통해서 많은 사용자를 받아서 성공 하겠다. 또는 이 서비스를 통해서 많은 수입을 얻게다는 아니였다. 단순히, 현재 회사에서 하지 못하는 불특정 실 사용자
가 있는 (B2C 와 같은) 서비스를 만들고 운영 해보고 싶다는 마음이 잡봇
을 만들게 되었다. 하지만, 개발 기간이 길어지고 길어진 만큼 자신감이 많이 낮아져 사람들이 쓸까? 라는 생각이 자꾸 들게 되었다. 이 때문에 MVP를 꿈꾸었지만, 실패 했다고 생각한다.
처음에는 SPA를 위한 React(Vite) 로 개발 하였어요. 하지만, Google Analytics, Google Search Console, Google Adsense 등을 사용 하다 보니까, 생각보다 최적화가 되지 않는 느낌을 들었어요.
즉, SEO에 이점을 주기 위해서 NextJS로 다시 개발 하였어요.
sitemap.xml 작성 자동화
/** @type {import('next-sitemap').IConfig} */
module.exports = {
siteUrl: process.env.NEXT_PUBLIC_PRODUCTION_URL || 'https://job-bot.site',
generateRobotsTxt: true, // (optional)
// ...other options
}
각 페이지에 대한 HTML
빌드
친절한 공식 문서
사실 SEO 개선을 위해서 NextJS 로 개발 하였지만, 각 페이지에 대한 HTML을 빌드 해주고 난 뒤, 페이지에 접근을 하면, 아무것도 없는 HTML에 JS를 읽고 난 후 각각에 값들을 생성 하는 것이 아쉬워요.
GatsbyJS 또는 SPA를 통해 Build 한 HTML 파일에 직접 태그들을 넣어서 JS를 읽기전에 Metatag 를 넣지 못하여 구글 이외의 크롤러로 부터 metatag 등을 읽히지 못하여 SEO 개선에 이점이 없었습니다.
public/index.html
과 같이 페이지의 근본(?) 이 되는 html 을 조작 할 수 있지만, nextjs 13
에서는 그 부분을 제어 할 수 없어 Meta 저는 이 문제를 해결 하기 위해서 빌드 이후, script 코드를 읽어 정적으로 넣고 싶은 metadata를 생성된 html에 직접 주입 하는 방법을 사용 하였어요.
build 이후 생성된 html에 metadata 를 추가하는 스크립트 추가
// package.json
"scripts": {
"dev": "next dev",
"build": "next build && next export && node ./dist/insert-metadata.js",
"start": "next start",
"postbuild": "next-sitemap",
"lint": "next lint"
},
insert-metadata.js
/* eslint-disable @typescript-eslint/no-var-requires */
const fs = require('fs')
const headTag = `
<meta name='description' content='생성 AI를 이용하여 지원서 작성을 도와줘요'>
</meta>
<meta name='author' content='@jaewoong2'>
</meta>
<meta name='keywords' content='잡봇, 지원서 봇, 생성AI, 지원서, 자소서'>
</meta>
<meta name='viewport' content='width=device-width, initial-scale=1'>
</meta>
<meta property='og:title' content='잡봇 | 지원서 봇'>
</meta>
<meta property='og:description' content='생성 AI를 이용하여 지원서 작성을 도와줘요'>
</meta>
<meta property='og:url' content='https://www.job-bot.site/'>
</meta>
<meta property='og:site_name' content='잡봇'>
</meta>
<meta property='og:locale' content='ko-KR'>
</meta>
<meta property='og:image'
content='https://ywnfqdpcmgtllkshgzsl.supabase.co/storage/v1/object/public/image/dark-jobbot.png'>
</meta>
<meta property='og:type' content='website'>
</meta>
<meta name='twitter:card' content='summary'>
</meta>
<meta name='twitter:site' content='https://www.job-bot.site'>
</meta>
<meta name='twitter:title' content='잡봇 | 지원서 봇'>
</meta>
<meta name='twitter:description' content='생성 AI를 이용하여 지원서 작성을 도와줘요'>
</meta>
<meta name='twitter:image'
content='https://ywnfqdpcmgtllkshgzsl.supabase.co/storage/v1/object/public/image/dark-jobbot.png'>
</meta>
`
const htmlFile = './dist/index.html'
// html 파일을 읽습니다.
const html = fs.readFileSync(htmlFile, 'utf8')
// head 태그를 삽입합니다.
const newHtml = html.replace('<head>', `<head>${headTag}`)
// html 파일을 씁니다.
fs.writeFileSync(htmlFile, newHtml, 'utf8')
스크립트 코드를 작성 함으로써, 공통 metadata
를 정적으로 html에 주입 할 수 있게 되었어요.
<Image />
. 를 통해서 이미지 최적화를 해주는데, SSR 에서만 사용 가능 하다는 것