Skip to content

KSWA-SWEEP/jaksim31-front

Repository files navigation


μž‘μ‹¬μ‚ΌμΌπŸ“™


πŸ“– Intro

μž‘μ‹¬μ‚ΌμΌ 닀이어리 μ„œλΉ„μŠ€ ν”„λ‘ νŠΈμ—”λ“œ ν”„λ‘œμ νŠΈ

2022. 08 ~ 2023. 02


🚩 Demo

🎞 Video

Video Label
YOUTUBE λ°”λ‘œκ°€κΈ° 링크


πŸ–₯ Screenshots

image image image image image image image image


πŸš€ Quick Start

πŸ–‡ Git clone

git clone https://github.com/KSWA-SWEEP/jaksim31-front.git

πŸ“₯ Install dependencies

cd jaksim31-front
npm install

βš™ Set environment variables

ν”„λ‘œμ νŠΈ 루트 κ²½λ‘œμ— ν™˜κ²½ λ³€μˆ˜ 파일 μ„€μ •

β‡’ 기본적으둜 env 파일 λ§Œλ“€μ–΄μ„œ μ„€μ •ν•΄μ£Όλ©΄ 되며, .env.local λ“± ν™˜κ²½μ— λ§žλŠ” 파일 μΆ”κ°€ν•˜μ—¬ ν™˜κ²½μ— λ”°λ₯Έ λ³€μˆ˜ μ„€μ • κ°€λŠ₯

πŸ“‹ .env

NEXT_PUBLIC_UNSPLASH_ACCESSKEY=${Unsplash API Access Key}
NEXT_PUBLIC_BASE_URL=${Backend url - local일 경우 http://localhost:8080}
NEXT_PUBLIC_API_URL=${Frontend url - local일 경우 http://localhost:3000}

# Kakao λ‘œκ·ΈμΈμ„ μœ„ν•œ λ³€μˆ˜
NEXT_PUBLIC_KAKAO_CLIENT_ID=${Kakao Client ID}
NEXT_PUBLIC_KAKAO_REDIRECT_URL=${Kakao 둜그인 ν›„ 이동할 redirect url}

# KiC Object Storage
NEXT_PUBLIC_KAKAO_API_AUTH_URL=https://iam.kakaoi.io/identity/v3/auth/tokens
NEXT_PUBLIC_KAKAO_API_AUTH_ACCESSKEY=${Kakao i Cloud API Access Key}
NEXT_PUBLIC_KAKAO_API_AUTH_SECRET=${Kakao i Cloud Auth Secret}
NEXT_PUBLIC_KAKAO_FILE_UPLOAD_URL=${이미지 μ—…λ‘œλ“œ 경둜}
NEXT_PUBLIC_KAKAO_FILE_VIEW_URL=${이미지 쑰회 경둜}
NEXT_PUBLIC_DEFAULT_PROFILE=${default ν”„λ‘œν•„ 이미지 url}

# Google Analytics
NEXT_PUBLIC_GA_TRACKING_ID=${Google Analytics Tracking ID}

πŸ›« run app

# development ν™˜κ²½ μ‹€ν–‰ μ‹œ
npm run dev

# production ν™˜κ²½ μ‹€ν–‰ μ‹œ
npm run build
npm start

πŸ›  Skills

Languages

  • JavaScript
  • CSS

Dependencies

πŸ“‹ package.json

...

"dependencies": {
    // Next.js - 13 버전 μ‚¬μš©
    "next": "13.1.1", 
    // React - 18 버전 μ‚¬μš©
    "react": "18.2.0",]
    "react-dom": "18.2.0",

    // πŸ“„ UI
    // Tailwind Component Library - modal, popup λ“± μ‚¬μš©
    "daisyui": "^2.46.1",
    "@headlessui/react": "^1.7.7",
    // SVG icon Library
    "@heroicons/react": "^2.0.13",
    // 일기 μž‘μ„±μ„ μœ„ν•œ Editor Library
    "@ckeditor/ckeditor5-react": "^5.0.5",
    "ckeditor5-custom-build": "file:ckeditor5",
    // nextjs 13의 next/font
    "@next/font": "^13.1.1",
    // Positioning Library - 툴팁 및 νŒμ˜€λ²„ μš”μ†Œκ°€ μ»΄ν¬λ„ŒνŠΈ μ˜μ—­ λ°–μ—μ„œ μž˜λ¦¬λŠ” ν˜„μƒ ν•΄κ²°
    "@popperjs/core": "^2.11.6",
    // SVG 이미지λ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•œ Library
    "@svgr/webpack": "^6.5.1",
    // λŒ€μ‹œλ³΄λ“œμ˜ μ°¨νŠΈλ“€μ„ 그리기 μœ„ν•œ Library
    "chart.js": "^2.9.4",
    // Animation Library
    "framer-motion": "^8.2.4",
    "react-transition-group": "^4.4.5",
    // μŠ€ν¬λ‘€λ°” 숨기기 Library
    "tailwind-scrollbar-hide": "^1.1.7"

    // πŸ“„ 기타 κΈ°λŠ₯
    // Classname joining library - μΌ€μ΄μŠ€μ— 따라 클래슀λͺ… λ³€κ²½ν•˜κ±°λ‚˜ 쑰건 μΆ”κ°€ν•  경우 μ‚¬μš©
    "classnames": "^2.3.2",
    // μΏ ν‚€ 값을 μ½κ±°λ‚˜ λ³€κ²½ν•˜κΈ° μœ„ν•œ Library
    "cookies-next": "^2.1.1",
    // ν™˜κ²½λ³€μˆ˜ μ„€μ •(.env 파일)을 μœ„ν•œ Library
    "dotenv": "^16.0.3",
    "dotenv-webpack": "^8.0.1",
    // 이메일 인증 Library - νšŒμ› κ°€μž… μ‹œ 이메일 인증 μ‚¬μš©
    "emailjs-com": "^3.2.0",
    // λ‚ μ§œ Library
    "moment": "^2.29.4",
    // SEO - sitemap μžλ™ 생성 Library
    "next-sitemap": "^3.1.50",
    // Calendar Library - custom λ””μžμΈ ν•΄μ„œ μ‚¬μš©
    "react-calendar": "^4.0.0",
    // Datepicker Library
    "react-datepicker": "^4.8.0",
    // Pagination Library - 
    "react-js-pagination": "^3.0.3",
    // Data Fetching Library
    "react-query": "^3.39.2",
    // Image Optimization Library
    "sharp": "^0.31.3",

    
    // πŸ“„ Test
    // E2E Test
    "@cypress/react18": "^2.0.0",
    // Mocking Library
    "intersection-observer": "^0.12.2",
    // Type check Library
    "prop-types": "^15.8.1",
}

...

πŸ—‚ Directory

πŸ“™ jaksim31-front
    β”œβ”€ πŸ“ app
    β”‚  β”œβ”€ common
    β”‚  β”‚  β”œβ”€ Drawer.jsx
    β”‚  β”‚  β”œβ”€ header
    β”‚  β”‚  β”‚  β”œβ”€ Header.jsx
    β”‚  β”‚  β”‚  β”œβ”€ loading.js
    β”‚  β”‚  β”‚  β”œβ”€ Login.js
    β”‚  β”‚  β”‚  └─ Profile.js
    β”‚  β”‚  β”œβ”€ LazyShow.js
    β”‚  β”‚  └─ Tutorial.js
    β”‚  β”œβ”€ home
    β”‚  β”‚  β”œβ”€ landing
    β”‚  β”‚  β”‚  β”œβ”€ ExampleScreen.js
    β”‚  β”‚  β”‚  └─ page.jsx
    β”‚  β”‚  β”œβ”€ page.jsx
    β”‚  β”‚  └─ tutorial
    β”‚  β”‚     └─ page.jsx
    β”‚  β”œβ”€ diary
    β”‚  β”‚  β”œβ”€ common
    β”‚  β”‚  β”‚  β”œβ”€ backButton.js
    β”‚  β”‚  β”‚  β”œβ”€ diaryInputFormat.js
    β”‚  β”‚  β”‚  β”œβ”€ Editor.js
    β”‚  β”‚  β”‚  └─ loading.js
    β”‚  β”‚  β”œβ”€ create
    β”‚  β”‚  β”‚  └─ [date]
    β”‚  β”‚  β”‚     β”œβ”€ createDiary.js
    β”‚  β”‚  β”‚     └─ page.jsx
    β”‚  β”‚  β”œβ”€ dashboard
    β”‚  β”‚  β”‚  β”œβ”€ BarChartCard.js
    β”‚  β”‚  β”‚  β”œβ”€ DonutChartCard.js
    β”‚  β”‚  β”‚  β”œβ”€ layout.jsx
    β”‚  β”‚  β”‚  β”œβ”€ page.jsx
    β”‚  β”‚  β”‚  β”œβ”€ ProfileCard.js
    β”‚  β”‚  β”‚  └─ RecentDiaryCard.js
    β”‚  β”‚  β”œβ”€ list
    β”‚  β”‚  β”‚  β”œβ”€ calendar
    β”‚  β”‚  β”‚  β”‚  β”œβ”€ Calendar.css
    β”‚  β”‚  β”‚  β”‚  β”œβ”€ calendarList.js
    β”‚  β”‚  β”‚  β”‚  β”œβ”€ loading.js
    β”‚  β”‚  β”‚  β”‚  └─ page.jsx
    β”‚  β”‚  β”‚  β”œβ”€ DateRangePicker.js
    β”‚  β”‚  β”‚  β”œβ”€ grid
    β”‚  β”‚  β”‚  β”‚  β”œβ”€ error.js
    β”‚  β”‚  β”‚  β”‚  β”œβ”€ gridList.js
    β”‚  β”‚  β”‚  β”‚  β”œβ”€ loading.js
    β”‚  β”‚  β”‚  β”‚  β”œβ”€ page.jsx
    β”‚  β”‚  β”‚  β”‚  └─ Pagination.css
    β”‚  β”‚  β”‚  β”œβ”€ layout.jsx
    β”‚  β”‚  β”‚  β”œβ”€ ListBox.js
    β”‚  β”‚  β”‚  β”œβ”€ page.jsx
    β”‚  β”‚  β”‚  └─ ViewTypeTab.js
    β”‚  β”‚  β”œβ”€ page.jsx
    β”‚  β”‚  └─ [diaryId]
    β”‚  β”‚     β”œβ”€ diaryContents.js
    β”‚  β”‚     β”œβ”€ loading.js
    β”‚  β”‚     β”œβ”€ modify
    β”‚  β”‚     β”‚  β”œβ”€ date.js
    β”‚  β”‚     β”‚  β”œβ”€ loading.js
    β”‚  β”‚     β”‚  └─ page.jsx
    β”‚  β”‚     └─ page.jsx
    β”‚  β”œβ”€ globals.css
    β”‚  β”œβ”€ head.jsx
    β”‚  β”œβ”€ layout.jsx
    β”‚  β”œβ”€ loading.js
    β”‚  β”œβ”€ page.module.css
    β”‚  β”œβ”€ page.jsx
    β”‚  β”œβ”€ api
    β”‚  β”‚  β”œβ”€ addDiary.js
    β”‚  β”‚  β”œβ”€ analyzeDiary.js
    β”‚  β”‚  β”œβ”€ checkIsMember.js
    β”‚  β”‚  β”œβ”€ checkPassword.js
    β”‚  β”‚  β”œβ”€ deleteDiary.js
    β”‚  β”‚  β”œβ”€ getDiary.js
    β”‚  β”‚  β”œβ”€ getDiaryList.js
    β”‚  β”‚  β”œβ”€ getEmotionCount.js
    β”‚  β”‚  β”œβ”€ getKakaoApiAccessKey.js
    β”‚  β”‚  β”œβ”€ getUserInfo.js
    β”‚  β”‚  β”œβ”€ login.js
    β”‚  β”‚  β”œβ”€ logout.js
    β”‚  β”‚  β”œβ”€ modifyDiary.js
    β”‚  β”‚  β”œβ”€ signUp.js
    β”‚  β”‚  β”œβ”€ updatePassword.js
    β”‚  β”‚  β”œβ”€ updateUserInfo.js
    β”‚  β”‚  └─ uploadImg.js
    β”‚  β”œβ”€ hooks
    β”‚  β”‚  β”œβ”€ mutations
    β”‚  β”‚  β”‚  β”œβ”€ useDiaryDelete.js
    β”‚  β”‚  β”‚  β”œβ”€ useDiarySave.js
    β”‚  β”‚  β”‚  β”œβ”€ useLogin.js
    β”‚  β”‚  β”‚  β”œβ”€ useLogout.js
    β”‚  β”‚  β”‚  └─ useUserInfoUpdate.js
    β”‚  β”‚  └─ queries
    β”‚  β”‚     β”œβ”€ useDiaryListPageQuery.js
    β”‚  β”‚     β”œβ”€ useDiaryListQuery.js
    β”‚  β”‚     β”œβ”€ useDiaryQuery.js
    β”‚  β”‚     β”œβ”€ useEmotionCountQuery.js
    β”‚  β”‚     └─ useUserInfoQuery.js
    β”‚  └─ ReactQueryWrapper.jsx
    β”‚  
    β”œβ”€ 😺 .github
    β”‚  └─ workflows
    β”‚     └─ github-action.yml
    β”‚  
    β”œβ”€ πŸ§ͺ cypress
    β”‚  β”œβ”€ downloads
    β”‚  β”œβ”€ e2e
    β”‚  β”‚  β”œβ”€ diary
    β”‚  β”‚  β”‚  β”œβ”€ diaryEdit.cy.js
    β”‚  β”‚  β”‚  β”œβ”€ diaryList.cy.js
    β”‚  β”‚  β”‚  └─ diarySave.cy.js
    β”‚  β”‚  └─ member
    β”‚  β”‚     β”œβ”€ userInfo.cy.js
    β”‚  β”‚     └─ userLogin.cy.js
    β”‚  β”œβ”€ fixtures
    β”‚  β”‚  └─ example.json
    β”‚  └─ support
    β”‚     β”œβ”€ commands.js
    β”‚     └─ e2e.js
    β”‚  
    β”œβ”€ πŸ“¦ public
    β”‚  β”œβ”€ jaksim31.ico
    β”‚  β”œβ”€ images
    β”‚  β”‚  β”œβ”€ emotion
    β”‚  β”‚  β”‚  β”œβ”€ bad-small.png
    β”‚  β”‚  β”‚  β”œβ”€ bad.png
    β”‚  β”‚  β”‚  β”œβ”€ bored-small.png
    β”‚  β”‚  β”‚  β”œβ”€ bored.png
    β”‚  β”‚  β”‚  β”œβ”€ embarrassed-small.png
    β”‚  β”‚  β”‚  β”œβ”€ embarrassed.png
    β”‚  β”‚  β”‚  β”œβ”€ good-small.png
    β”‚  β”‚  β”‚  β”œβ”€ good.png
    β”‚  β”‚  β”‚  β”œβ”€ nothing-small.png
    β”‚  β”‚  β”‚  β”œβ”€ nothing.png
    β”‚  β”‚  β”‚  β”œβ”€ sad-small.png
    β”‚  β”‚  β”‚  β”œβ”€ sad.png
    β”‚  β”‚  β”‚  β”œβ”€ scared-small.png
    β”‚  β”‚  β”‚  β”œβ”€ scared.png
    β”‚  β”‚  β”‚  β”œβ”€ surprised-small.png
    β”‚  β”‚  β”‚  β”œβ”€ surprised.png
    β”‚  β”‚  β”‚  β”œβ”€ unsure-small.png
    β”‚  β”‚  β”‚  └─ unsure.png
    β”‚  β”‚  β”œβ”€ gradient.jpg
    β”‚  β”‚  β”œβ”€ kakaoLogin.png
    β”‚  β”‚  β”œβ”€ landing-example.webp
    β”‚  β”‚  β”œβ”€ paperTexture.jpg
    β”‚  β”‚  └─ tutorial
    β”‚  β”‚     β”œβ”€ calendar.webp
    β”‚  β”‚     β”œβ”€ create.webp
    β”‚  β”‚     β”œβ”€ drawer.webp
    β”‚  β”‚     β”œβ”€ dashboard.webp
    β”‚  β”‚     β”œβ”€ grid.webp
    β”‚  β”‚     β”œβ”€ login.webp
    β”‚  β”‚     └─ signUp.webp
    β”‚  β”œβ”€ next.svg
    β”‚  β”œβ”€ svgs
    β”‚  β”‚  └─ spinner.svg
    β”‚  β”œβ”€ thirteen.svg
    β”‚  └─ vercel.svg
    β”‚  
    β”œβ”€ πŸ“– README.md
    β”œβ”€ 🐳 Dockerfile
    β”œβ”€ package.json
    β”œβ”€ next.config.js
    β”œβ”€ cypress.config.js
    β”œβ”€ postcss.config.js
    └─ tailwind.config.js

βš– License

image


🀳🏻 Mobile App

image μž‘μ‹¬μ‚ΌμΌμ€ λ°˜μ‘ν˜• μ›ΉμœΌλ‘œ κ΅¬ν˜„λ˜μ—ˆκΈ°μ—, React Nativeλ₯Ό 톡해 μ•±μœΌλ‘œλ„ κ΅¬ν˜„ 및 μΆœμ‹œκ°€ μ§„ν–‰λ˜μ—ˆμŠ΅λ‹ˆλ‹€. Google Play Sotreμ—μ„œ λ‹€μš΄λ‘œλ“œ ν•  수 있으며, μ›Ήμ•±μœΌλ‘œ κ΅¬ν˜„λ˜μ–΄ μ›Ήμ‚¬μ΄νŠΈμ—μ„œ μ‚¬μš©ν•  수 μžˆλŠ” λͺ¨λ“  κΈ°λŠ₯듀을 λ™μΌν•˜κ²Œ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. Google Play - Jaksim31 링크


πŸ“Œ TODO

  • ν‚€μ›Œλ“œ Custom κΈ°λŠ₯ - μΆ”μΆœλœ ν‚€μ›Œλ“œμ— λŒ€ν•΄ μˆ˜μ • 및 μΆ”κ°€ν•˜λŠ” κΈ°λŠ₯
  • 일기 곡유 및 곡개 κ²Œμ‹œ κΈ°λŠ₯ - μž‘μ„±ν•œ 일기λ₯Ό κ³΅μœ ν•˜κ±°λ‚˜ 곡개 κ²Œμ‹œν•˜λŠ” λ“±μ˜ κΈ°λŠ₯
  • 친ꡬ & λŒ“κΈ€, μ’‹μ•„μš” κΈ°λŠ₯ - μ‚¬μš©μžκ°„ 친ꡬλ₯Ό λ§Ίμ–΄ μ„œλ‘œμ˜ 일기λ₯Ό μ‘°νšŒν•˜κ³  λŒ“κΈ€, μ’‹μ•„μš” 등을 남길 수 μžˆλŠ” κΈ°λŠ₯

πŸ”₯ Features

πŸ“™ Diary

  • 썸넀일 이미지 생성

    • μ‚¬μš©μžλ“€μ—κ²Œ λ‹€μ–‘ν•œ 썸넀일 이미지λ₯Ό μ œκ³΅ν•˜κΈ° μœ„ν•΄ Unsplash 의 Search photos by keyword APIλ₯Ό μ‚¬μš©ν•˜μ—¬ 이미지 κ°€μ Έμ˜΄
      • Application 등둝 image image Jaksim31은 ν˜„μž¬ Unsplash 개발자 μ‚¬μ΄νŠΈμ— λ“±λ‘ν•˜μ—¬ 인증 받은 μƒνƒœ (Production)

      • μ‚¬μš©

        πŸ“‹ Editor.js

        image μΆ”μΆœλœ ν‚€μ›Œλ“œ 와 감정 쀑 랜덀으둜 단어 ν•˜λ‚˜ μ„ νƒν•˜μ—¬ API ν˜ΈμΆœν•¨ image

  • 일기 μž‘μ„± ( CKEditor )

    • μ‚¬μš©μžλ“€μ΄ 일기에 λ‹€μ–‘ν•œ 글씨 크기, 색상, 폰트 등을 μ„€μ •ν•  수 μžˆλ„λ‘ JavaScript 기반의 에디터인 CKEditorλ₯Ό μ μš©ν•¨ (버전 5)
      • CKEditor Customize ν•„μš” κΈ°λŠ₯듀을 λ„£μ–΄ CKEditor Customizeν•˜μ—¬ μ‚¬μš© image
        CKEditor5 Online Builder 링크

      • μ‚¬μš© πŸ“‹ Editor.js

        image image

✨ Other

  • Responsive & Dark Mode

    • λ‹€μ–‘ν•œ λ””λ°”μ΄μŠ€ ν™˜κ²½ 및 λͺ¨λ“œμ—μ„œ μœ μ—°ν•œ μž‘λ™μ„ ν•  수 μžˆλ„λ‘ UI ν”„λ ˆμž„μ›Œν¬μΈ Tailwindcssλ₯Ό 톡해 λ°˜μ‘ν˜• 및 닀크λͺ¨λ“œ λŒ€μ‘

    • Responsice web

      • PC, Tablet, Mobile λ“± μ—¬λŸ¬ ν™”λ©΄ 크기에 따라 μ μ ˆν•œ 크기둜 보여지도둝 sm: , md: , lg: λ“±μ˜ μ˜΅μ…˜μ„ 섀정해두어 λ°˜μ‘ν˜• 적용 image

      • 적용 πŸ“‹ gridList.js

        image image

    • Dark Mode

      • 닀크λͺ¨λ“œ 적용 μ‹œ 변경될 색상 및 μ˜΅μ…˜μ„ dark: 둜 섀정해두어 닀크λͺ¨λ“œ 적용 image

        • 적용 πŸ“‹ ProfileCard.js image image
  • Server State 관리

    • μ„œλ²„λ‘œλΆ€ν„° data fetching을 톡해 κ°€μ Έμ˜€λŠ” Server state에 λŒ€ν•œ μ „μ—­ μƒνƒœ 관리λ₯Ό μœ„ν•΄ Data fetching 라이브러리인 React Queryλ₯Ό μ μš©ν•˜μ—¬ Server State 관리
      • 적용 πŸ“‹ app/layout.jsx

        image 루트 경둜의 layout(πŸ“„ app/layout.jsx)μ—μ„œ ν•˜μœ„ μ»΄ν¬λ„ŒνŠΈλ“€μ— λŒ€ν•΄ 자체적으둜 λ§Œλ“  React Query μ»΄ν¬λ„ŒνŠΈμΈ **<ReactQueryWrapper>**둜 감싸주어 λͺ¨λ“  νŽ˜μ΄μ§€μ— λŒ€ν•΄ ν•˜λ‚˜μ˜ query clientλ₯Ό κ°–λŠ” μƒνƒœ 적용

        πŸ“‹ app/ReactQueryWrapper.js

        image **QueryClientProvider**λ₯Ό 톡해 queryClient default 섀정을 ν•΄μ£Όκ³ , 개발 μ‹œ 편리λ₯Ό μœ„ν•΄ **ReactQueryDevtools**λ₯Ό μ„€μ •ν•˜μ˜€μœΌλ©°, queryClient μ„€μ • μ‹œ query client에 λŒ€ν•œ κΈ°λ³Έ μ„€μ •

        • React Query hooks image πŸ“ app/hooks 경둜 내에 React Query hook듀에 λŒ€ν•œ ν•¨μˆ˜λ₯Ό λ§Œλ“€μ–΄λ‘μ–΄ ν•΄λ‹Ή hook이 ν•„μš”ν•œ μ»΄ν¬λ„ŒνŠΈλ“€ λ‚΄μ—μ„œ κ°„λ‹¨ν•˜κ²Œ κ°€μ Έλ‹€ μ“Έ 수 μžˆλ„λ‘ 함

        πŸ“‹ useDiaryListQuery.js

        image api fetch ν•œ 이후 λ§Œμ•½ responseλ₯Ό μ œλŒ€λ‘œ λ°›μ•„μ˜€μ§€ λͺ»ν•œ 경우(= μ—λŸ¬ λ°œμƒμ˜ 경우 = response data에 errorCodeκ°€ ν¬ν•¨λœ 경우) error throw

        πŸ“‹ calendarList.js

        Untitled

        useDiaryListQuery μ„ μ–Έ μ‹œ error 메세지λ₯Ό λ°›μ•„μ˜¬ error와 error μƒνƒœμ— λŒ€ν•œ isError κ°’ μ„ μ–Έ

        image isError 일 경우 error 메세지λ₯Ό λ³΄μ—¬μ£ΌλŠ” UI ꡬ성

  • 이미지 Object Stroage μ €μž₯

    • μ‚¬μš©μžκ°€ 일기λ₯Ό μ €μž₯ν•  경우 μƒμ„±λ˜λŠ” 썸넀일 이미지와 μ‚¬μš©μžμ˜ ν”„λ‘œν•„ 이미지λ₯Ό KiC Object Storage μ €μž₯ν•˜μ—¬ μ‚¬μš©
      • 적용

        πŸ“‹ uploadImg.js

        image
        KiC API μš”μ²­μ„ ν—ˆμš©λ°›κΈ° μœ„ν•œ Access key λ°œκΈ‰ 이후, ν•΄λ‹Ή keyλ₯Ό μ‚¬μš©ν•˜μ—¬ Object Storage에 이미지 upload μš”μ²­

        • UI μ €μž₯에 μ‹œκ°„μ΄ μ’€ 걸리기 λ•Œλ¬Έμ— 일기 μ €μž₯ λͺ¨λ‹¬μ—μ„œ μ €μž₯ μ „(썸넀일 선택) / μ €μž₯쀑 / μ €μž₯ μ™„λ£Œ μƒνƒœμ— 따라 UI 및 μ €μž₯ λ²„νŠΌ λΉ„ν™œμ„±ν™”λ₯Ό λ‹€λ₯΄κ²Œ ν•˜μ—¬ μ‚¬μš©μžκ°€ 일기 μ΅œμ’… μ €μž₯의 κ³Όμ •μ—μ„œ κΈ°λ‹€λ¦¬κ²Œ λ˜λŠ” μ‹œκ°„μ„ μ€„μž„! (μ‚¬μš©μž κ²½ν—˜ ν–₯상↑)
          • 썸넀일 μ €μž₯ κ³Όμ •
            • μ €μž₯ μ „ image
              **이 μ‚¬μ§„μœΌλ‘œ κ²°μ •**β‡’ λ²„νŠΌ ν‘œμ‹œ / μ €μž₯ν•˜κΈ° β‡’ λ²„νŠΌ λΉ„ν™œμ„±ν™”

            • μ €μž₯쀑 image
              **μ €μž₯μ€‘μž…λ‹ˆλ‹€** β‡’ λ²„νŠΌ ν‘œμ‹œ / μ €μž₯ν•˜κΈ° β‡’ λ²„νŠΌ λΉ„ν™œμ„±ν™”

            • μ €μž₯ μ™„λ£Œ image
              썸넀일 생성 μ™„λ£Œ β‡’ λ²„νŠΌ ν‘œμ‹œ / μ €μž₯ν•˜κΈ° β‡’ λ²„νŠΌ ν™œμ„±ν™”

  • SEO

    • μ›Ήμ‚¬μ΄νŠΈκ°€ 검색 μ—”μ§„μ—μ„œ μ‰½κ²Œ λ…ΈμΆœλ  수 μžˆλ„λ‘ 검색 엔진 μ΅œμ ν™” 적용
      • 적용
        • NAVER

          πŸ“‹ DefaultTags.jsx

          image header.js νŒŒμΌμ— κ³΅ν†΅μ μœΌλ‘œ λ“€μ–΄κ°€λŠ” DefaultTags.jsx νŒŒμΌμ— naverμ—μ„œ μ œκ³΅ν•˜λŠ” meta tag μΆ”κ°€

        • Google

          image 도메인 κ΅¬μž…ν•œ μ‚¬μ΄νŠΈμ—μ„œ μ œκ³΅ν•˜λŠ” txt λ ˆμ½”λ“œ μΆ”κ°€ν•˜μ—¬ 인증

        • OpenGraph

          • μ›Ήμ‚¬μ΄νŠΈμ˜ 메타정보λ₯Ό μ‰½κ²Œ ν‘œμ‹œν•˜κΈ° μœ„ν•΄μ„œ 메타정보에 ν•΄λ‹Ήν•˜λŠ” 제λͺ©, μ„€λͺ…, λ¬Έμ„œμ˜ νƒ€μž…, λŒ€ν‘œ URL λ“± λ‹€μ–‘ν•œ μš”μ†Œλ“€μ— λŒ€ν•΄μ„œ μ‚¬λžŒλ“€μ΄ ν†΅μΌν•΄μ„œ μ“Έ 수 μžˆλ„λ‘ Open Graph νƒœκ·Έλ₯Ό μ •μ˜ν•¨
            • 적용 πŸ“‹ DefaultTags.jsx

              image

        • robots.txt & sitemap.xml

          • μ›Ήμ‚¬μ΄νŠΈμ—μ„œ ν¬λ‘€λ§ν•˜λ©° 정보λ₯Ό μˆ˜μ§‘ν•˜λŠ” 검색엔진 ν¬λ‘€λŸ¬κ°€ μ›ΉνŽ˜μ΄μ§€λ₯Ό μˆ˜μ§‘ν•  수 μžˆλ„λ‘ ν•˜λŠ” **robots.txt** 파일과 **sitemap.xml** νŒŒμΌμ„ 섀정함

          πŸ“‹ robots.txt

          image

          πŸ“‹ sitemap.xml

          image
          β‡’ next-sitemap 라이브러리λ₯Ό μ‚¬μš©ν•˜μ—¬ ν”„λ‘œμ νŠΈ λΉŒλ“œ μ‹œ μžλ™ μƒμ„±λ˜λ„λ‘ μ„€μ •

  • Google Analytics

    • 방문자의 μœ μž… 좜처λ₯Ό ν™•μΈν•˜κ±°λ‚˜, μ‚¬μš©μž 행동을 νŒŒμ•…ν•˜λŠ” λ“± μœ μš©ν•œ 정보λ₯Ό μˆ˜μ§‘ν•˜κ³  μ €μž₯ν•˜μ—¬ λΆ„μ„ν•˜κ³ μž κ΅¬κΈ€μ—μ„œ 무료둜 μ œκ³΅ν•˜λŠ”Β μ›Ή λ‘œκ·ΈλΆ„μ„ 툴인 Google Analytics 적용
      • 적용

        πŸ“‹ layout.jsx

        export default function RootLayout({ children }) {  
        
          return (
            <html>
              <head />
              <body>
        
                ...
        
                **{/* Google Analytics */}
                <script strategy="afterInteractive" src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_GA_TRACKING_ID}`} />
                <script
                  id="gtag-init"
                  strategy="afterInteractive"
                  dangerouslySetInnerHTML={{
                    __html: `
                  window.dataLayer = window.dataLayer || [];
                  function gtag(){dataLayer.push(arguments);}
                  gtag('js', new Date());
                  gtag('config', '${process.env.NEXT_PUBLIC_GA_TRACKING_ID}', {
                    page_path: window.location.pathname,
                  });
                `
                  }}
                />**
        
        				...
        
              </body>
            </html>
          )
        }

        image