๋ณธ ๊ธ์ ํจ์คํธ์บ ํผ์ค โ Next.js ์ค๋ฌด ๊ฐ์ ์ค Part 8. Next.js 13์ผ๋ก ์๋ฐ ์์ฝ ํ๋ซํผ ๋ง๋ค๊ธฐ๋ฅผ ์๊ฐํ๋ฉฐ ํ์ตํ ๋ด์ฉ์ ์ ๋ฆฌํ ๊ฒ์ ๋๋ค. ๐๐ป
next/script
๋ ์ธ๋ถ ์คํฌ๋ฆฝํธ๋ฅผ ํ์ด์ง์ ํจ์จ์ ์ผ๋ก ์ฝ์
ํ ์ ์๋๋ก ๋์์ฃผ๋ ์ปดํฌ๋ํธ๋ค. ๋ธ๋ผ์ฐ์ ์์ <script>
ํ๊ทธ๋ฅผ ์๋์ผ๋ก ์ฝ์
ํ๋ ๊ฒ๊ณผ ๋ฌ๋ฆฌScript
์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ฉด ์คํฌ๋ฆฝํธ์ ๋ก๋ฉ ์ ๋ต์ ๋ช
ํํ๊ฒ ์ค์ ํ์ฌ ํ์ด์ง ์ฑ๋ฅ๊ณผ ์คํ ์์ ์ ํจ๊ณผ์ ์ผ๋ก ์ ์ดํ ์ ์๋ค.
๐ก next/script์ ์ฃผ์ ํน์ง
- ๋ก๋ฉ ์ ๋ต ์ ์ด (
strategy
)
beforeInteractive
: HTML์ด ๋ ๋๋ง๋๊ธฐ ์ , ํ์ด์ง๊ฐ interactive ์ํ๊ฐ ๋๊ธฐ ์ด์ ์ ์คํํด์ผ ํ๋ ์คํฌ๋ฆฝํธ (ex. A/B ํ ์คํธ, ์ด๊ธฐ ์ถ์ ์คํฌ๋ฆฝํธ)afterInteractive(๊ธฐ๋ณธ๊ฐ)
: HTML ํ์ฑ๊ณผ DOM ๊ตฌ์ฑ์ด ์๋ฃ๋์ด ์ฌ์ฉ์๊ฐ ์ธํฐ๋์ ๊ฐ๋ฅํ ์์ ์ดํ์ ๋ก๋๋จ (๊ธฐ๋ณธ๊ฐ)lazyOnload
: ๋ธ๋ผ์ฐ์ ์ idle ์๊ฐ์ ๋ก๋๋์ด ์ด๊ธฐ ๋ ๋๋ง ์ฑ๋ฅ์ ์ํฅ์ ์ฃผ์ง ์์ (์ฑ๋ฅ ์ต์ ํ์ ์ ๋ฆฌ, ๋นํ์ ์คํฌ๋ฆฝํธ์ ์ ํฉ)worker
: Web Worker๋ฅผ ํตํด ๋ฉ์ธ ์ฐ๋ ๋์ ๋ถ๋ฆฌ๋ ํ๊ฒฝ์์ ์คํ (ํ์ฌ experimental ๊ธฐ๋ฅ)- ์๋ ์ต์ ํ ๊ธฐ๋ฅ ๋ด์ฅ : ์ค๋ณต ๋ก๋ ๋ฐฉ์ง, ๋น๋๊ธฐ ์ฒ๋ฆฌ, ๋ ๋๋ง ์ฐจ๋จ ๋ฐฉ์ง ๋ฑ์ ์ฑ๋ฅ ์ต์ ํ ๊ธฐ๋ฅ์ด ๋ด์ฅ
๐ก next/script ์ฃผ์ ์์ฑ (Props)
src
๋ก๋ํ ์คํฌ๋ฆฝํธ์ URL ๋๋ ๊ฒฝ๋ก ์ง์ strategy
์คํฌ๋ฆฝํธ์ ๋ก๋ฉ ์ ๋ต ์ค์
(์ธ์ ๋ก๋ ๋ฐ ์คํํ ์ง๋ฅผ ๊ฒฐ์ ,beforeInteractive
,afterInteractive
,lazyOnload
,worker
์ค ์ ํ)onLoad
์คํฌ๋ฆฝํธ๊ฐ ์ฑ๊ณต์ ์ผ๋ก ๋ก๋๋ ํ ์คํํ ์ฝ๋ฐฑ ํจ์๋ฅผ ์ง์ onReady
์คํฌ๋ฆฝํธ๊ฐ ์ค๋น๋์์ ๋ ์คํํ ์ฝ๋ฐฑ ํจ์๋ฅผ ์ง์
(๋ธ๋ผ์ฐ์ ๊ฐ ์ด๋ฏธ ํด๋น ์คํฌ๋ฆฝํธ๋ฅผ ์บ์์ ๊ฐ์ง๊ณ ์๋ ๊ฒฝ์ฐ ์ ์ฉ)onError
์คํฌ๋ฆฝํธ ๋ก๋ฉ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ ๋ ์คํํ ์ฝ๋ฐฑ ํจ์๋ฅผ ์ง์
onLoad์ onReady์ ์ฐจ์ด ๐ค
onLoad
: ์คํฌ๋ฆฝํธ๊ฐ ๋คํธ์ํฌ๋ฅผ ํตํด ์ฒ์ ๋ก๋๋ ๋๋ง ์คํ (strategy="beforeInteractive"
์๋ ํจ๊ป ์ฌ์ฉํ ์ ์์)onReady
: ์คํฌ๋ฆฝํธ๊ฐ ์ด๋ฏธ ๋ก๋๋ ์ํ์์๋ ํญ์ ์คํ (์ด๊ธฐํ ์ดํ์ ํ์ ์์ ex. ์ง๋ ๋ ๋๋ง, ์์ ฏ ์คํ ๋ฑ)์ ์ ํฉ
next/script
์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํด Kakao Maps JavaScript SDK๋ฅผ ๋์ ์ผ๋ก ๋ก๋ํ๋ค. ์คํฌ๋ฆฝํธ๋ strategy="afterInteractive"
๋ก ์ค์ ํด ํ์ด์ง๊ฐ interactive ์ํ๊ฐ ๋ ์ดํ์ ๋ก๋๋๋๋ก ํ๊ณ SDK ๋ก๋ ์๋ฃ ์์ ์ onReady
์ฝ๋ฐฑ์์ ๊ฐ์งํ๋ค.kakao.maps.load()
๋ฉ์๋๋ฅผ ํธ์ถํด ์ง๋๋ฅผ ์ด๊ธฐํํ๋ค. ์ด ๋ฉ์๋๋ Kakao์์ ์ ๊ณตํ๋ ๊ณต์ ๋ฐฉ์์ผ๋ก SDK๊ฐ ์์ ํ ๋ก๋๋ ์ดํ ์์ ํ๊ฒ kakao ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก ๋ณด์ฅํด์ค๋ค.kakao
๊ฐ ์ ์๋์ง ์์๋ค๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ ์๋ค. ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด, ์คํฌ๋ฆฝํธ ์๋จ์ /* global kakao */
์ฃผ์์ ์ถ๊ฐํ์ฌ ESLint๊ฐ kakao๋ฅผ ๊ธ๋ก๋ฒ ๋ณ์๋ก ์ธ์ํ๋๋ก ์ฒ๋ฆฌ๊ฐ ํ์ํ๋ค.Kakao Developers - ์นด์นด์ค
Kakao ์ง๋ Web API Documentation
Kakao ์ง๋ Web API Documentation - load
useQuery๋ฅผ ์ฌ์ฉํด /api/rooms
์์ ๋ชจ๋ ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ loadKakaoMap
ํจ์ ๋ด๋ถ์์ ํด๋น ๋ฐ์ดํฐ๋ฅผ ์ํํ๋ฉฐ ๊ฐ ์์ ์์น์ ๊ธฐ๋ณธ ๋ง์ปค๋ฅผ ํ์ํ๋๋ก ๊ตฌํํ๋ค.
Kakao ์ง๋ Web API Documentation - ๋ค๋ฅธ ์ด๋ฏธ์ง๋ก ๋ง์ปค ์์ฑํ๊ธฐ
Kakao ์ง๋ Web API Documentation - ์ปค์คํ ์ค๋ฒ๋ ์ด ์์ฑํ๊ธฐ
Kakao Map API์ MarkerImage
๋ฅผ ํ์ฉํด ๊ธฐ๋ณธ ๋ง์ปค ๋์ ์ปค์คํ
์ด๋ฏธ์ง๋ฅผ ์ ์ฉํ ์ ์๋ค. ๋ง์ปค ์ด๋ฏธ์ง์ ํฌ๊ธฐ์ ๊ธฐ์ค์ (offset) ์ ํจ๊ป ์ค์ ํด ๊ตฌ์ฑํ ์ ์๋ค.
CustomOverlay
๋ฅผ ์ฌ์ฉํ๋ฉด ๋ง์ปค ๋์ ์ง๋ ์์ ์ฌ์ฉ์ ์ ์ ๋งํฌ์
๋ ์ฝ์
ํ ์ ์๋ค.
์ด๋ฅผ ํ์ฉํด ์์์ ๊ฐ๊ฒฉ ์ ๋ณด๋ฅผ ์ปค์คํ
์ค๋ฒ๋ ์ด ํํ๋ก ์ง๋์ ํจ๊ป ํ์ํ๋๋ก ์ค์ ํ๋ค.
์ด๋ ์ค๋ฒ๋ ์ด์ ๋ค์ด๊ฐ ๊ฐ๊ฒฉ ์ ๋ณด์ ์คํ์ผ์ ์ ์ฉํ๊ธฐ ์ํด Tailwind์ @apply ์ง์์ด๋ฅผ ํ์ฉํด ์ ํธ๋ฆฌํฐ ํด๋์ค๋ค์ .custom_overlay
ํด๋์ค๋ก ๋ฌถ์ด HTML์ ์ ์ฉํ๋ค.
// custom overlay๋ฅผ ์ค์ ํฉ๋๋ค.
const content = `<div class="custom_overlay">${data.price?.toLocaleString()}์</div>`
/* global.css */
.custom_overlay {
@apply bg-white rounded-full px-2 py-1 text-xs font-semibold border border-gray-300 shadow hover:shadow-lg hover:font-bold;
}
๋ฉ์ธ ํ์ด์ง์์ ์์ ๋ฐ์ดํฐ๋ฅผ ๋ ๋๋งํ ๋ ์ด๋ฏธ์ง๊ฐ ๋ก๋๋๊ธฐ ์ ๊น์ง ๊ณต๊ฐ์ด ํ๋ณด๋์ง ์์ ๋ ์ด์์ ์ฌํํธ(Layout Shift)๊ฐ ๋ฐ์ํ๋ ๋ฌธ์ ๊ฐ ์๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด Next.js์ next/image
์ปดํฌ๋ํธ๋ก ๋ณ๊ฒฝํ๋ค. next/image
๋ ์ด๋ฏธ์ง ํฌ๊ธฐ๋ฅผ ๋ฏธ๋ฆฌ ๊ณ์ฐํ์ฌ ๊ณต๊ฐ์ ํ๋ณดํด์ฃผ๊ธฐ ๋๋ฌธ์ ๋ ๋๋ง ์ค ๋ฐ์ํ๋ ๋ ์ด์์ ๋ณ๊ฒฝ ์์ด ์์ ์ ์ธ UI๋ฅผ ์ ์งํ ์ ์๋ค.
blurDataURL
์์ฑ์ ํ์ฉํด ์ด๋ฏธ์ง ๋ก๋ฉ ์ ์ฐํ ํ์ ๋ธ๋ฌ ํ๋ฆฌ๋ทฐ๋ฅผ ํ์ํจ์ผ๋ก์จ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ๊ณ ๋ก๋ฉ ์ค์๋ ์๊ฐ์ ํผ๋๋ฐฑ ์ ๊ณต์ด ๊ฐ๋ฅํ๋ค.
next/image
์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ ๋ ์ธ๋ถ ์ด๋ฏธ์ง ๋๋ฉ์ธ์ ๋ก๋ํ๋ ค๋ฉด ํด๋น ์ด๋ฏธ์ง์ ํธ์คํธ๋ช
(hostname) ์ next.config.js์ images.domains ์ค์ ์ ๋ช
์ํด์ผ ํ๋ค. ์ด ์ค์ ์ด ๋๋ฝ๋๋ฉด ์ด๋ฏธ์ง ์ต์ ํ๊ฐ ์ ์ฉ๋์ง ์๊ฑฐ๋ ๋ก๋ฉ์ด ์ฐจ๋จ๋ ์ ์๋ค.์ ์ฉ ์ ํ ์ฐจ์ด ๐
์ ์ฉ ํ ๐คฉ
Kakao ์ง๋ Web API Documentation - ํด๋ฆญ ์ด๋ฒคํธ ๋ฑ๋กํ๊ธฐ
๋ง์ปค ํด๋ฆญ ์ window.kakao.maps.event.addListener
๋ฅผ ํตํด ์ด๋ฒคํธ๋ฅผ ๋ฑ๋กํ๊ณ ์ ํ๋ ์์ ๋ฐ์ดํฐ๋ฅผ Recoil ์ ์ญ ์ํ์ ์ ์ฅํด ์์ธ ์ ๋ณด UI์ ์ฐ๋ํ๋ค. ์ง๋ ๋ด ๋น ์์ญ์ ํด๋ฆญํ๋ฉด ํด๋น ์ํ๋ฅผ ์ด๊ธฐํํด ์์ธ ์ ๋ณด๊ฐ ๋
ธ์ถ๋์ง ์๋๋ก ์ฒ๋ฆฌํ๋ค.
๋ฉ์ธ ํ๋ฉด์์ ์์๋ฅผ ํด๋ฆญํ๋ฉด ํด๋น ์์์ id ๊ฐ์ ํตํด ์์ธ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ , ์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ฐ ์์ญ์ ๋ฐ์ดํฐ๋ฅผ ์ฐ๋ํ๋๋ก ๊ตฌํํ๋ค. ๊ธฐ์กด์๋ ๋ฌดํ ์คํฌ๋กค์ ํตํด ๋ชฉ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๋ ๊ตฌ์กฐ์๊ธฐ ๋๋ฌธ์ ๋จ์ผ ์์์ ์์ธ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ์กฐ๊ฑด์ ๋ถ๊ธฐํ์ฌ ์ฒ๋ฆฌํ ์ ์๋๋ก ์ธํฐํ์ด์ค์ ์กฐ๊ฑด๋ฌธ์ ๋ณด์ํ๋ค.
์์ ์์ธ ์ ๋ณด ํ์ด์ง๋ ์๋จ์ ์ด๋ฏธ์ง, ์ข์ธก์ ์์ธ ๋ด์ฉ๊ณผ ์ฐ์ธก ์์ฝ ๋ฐฐ๋ ์ปดํฌ๋ํธ๋ก ๋๋์ด UI๋ฅผ ๊ตฌ์ฑํ๋ค.
์์์ ๋ถ๊ฐ ๊ธฐ๋ฅ(์: ์ ํ ์ฒดํฌ์ธ ๊ฐ๋ฅ ์ฌ๋ถ, ์ฌ๋ฌด ๊ณต๊ฐ ์ ๊ณต ๋ฑ)์ ์ฌ์ฉ์์๊ฒ ์๋ดํ๊ธฐ ์ํด ๊ธฐ๋ฅ ํ์ ๊ณผ ์ค๋ช ๋ฌธ๊ตฌ๋ฅผ ๋งคํํ๋ ๊ตฌ์กฐ๋ฅผ ์ค๊ณํ๋ค.
FEATURE_TYPE
์์๋ฅผ Enum์ฒ๋ผ ์ ์ํ๊ณ ๊ฐ ํค์ ๋ํ ์ค๋ช ์FeatureDesc
๊ฐ์ฒด์ ๋งคํํ๋ค.
์ด ๋ฐฉ์์ ์กฐ๊ฑด๋ฌธ ์์ด๋ ๊ธฐ๋ฅ ํค๋ง ๋๊ฒจ ๊ฐ๊ฒฐํ๊ฒ ์ค๋ช ์ ์ถ๋ ฅํ ์ ์์ด ์ ์ง๋ณด์์ ์ฉ์ดํ๋ค.const FEATURE_TYPE = { FREE_CANCEL: 'FREE_CANCEL', SELF_CHECKIN: 'SELF_CHECKIN', FREE_OFFICE_SPACE: 'FREE_OFFICE_SPACE', } as const type FeatureType = (typeof FEATURE_TYPE)[keyof typeof FEATURE_TYPE] export const FeatureDesc: Record<FeatureType, string> = { [FEATURE_TYPE.FREE_CANCEL]: '๋ฌด๋ฃ ์ทจ์๊ฐ ๊ฐ๋ฅํฉ๋๋ค.', [FEATURE_TYPE.SELF_CHECKIN]: '์ ํ ์ฒดํฌ์ธ์ด ๊ฐ๋ฅํฉ๋๋ค.', [FEATURE_TYPE.FREE_OFFICE_SPACE]: '์ฌ๋ฌด ์์ค์ด ์์ต๋๋ค.', }
Kakao ์ง๋ Web API Documentation - ์ง๋์ ์ปจํธ๋กค ์ฌ๋ฆฌ๊ธฐ
๊ธฐ์กด ์ง๋ ์ปดํฌ๋ํธ๋ฅผ ์์ ํด ๋จ์ผ ์์๋ง ๋ง์ปค๋ก ํ์๋๋๋ก ํ๊ณ Kakao Map ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํด ์ง๋ ์ปจํธ๋กค ๊ธฐ๋ฅ๋ ๊ฐ๋จํ ์ถ๊ฐํ๋ค. ๐คฉ ( ํ ์คํธ๋ก ๋ฃ์ ๋ฐ์ดํฐ๊ฐ ๋ง์นจ ํ๊ฐ ์์.. ) ๐คช
์ฒ์ ์ฌ์ฉํด๋ณธ Headless UI๋ฅผ ํ์ฉํด ์ ๊ทผ์ฑ ๋์ ๊ณต์ ๋ชจ๋ฌ์ ๊ฐํธํ๊ฒ ๊ตฌํํ๋ค.Dialog
, Transition
, DialogPanel
์ปดํฌ๋ํธ๋ฅผ ์กฐํฉํ๊ณ useState
๋ฅผ ํตํด ์ด๋ฆผ/๋ซํ ์ํ๋ฅผ ์ ์ดํ๋ค.
๋ชจ๋ฌ์๋ ๊ณต์ ๋์ ์์์ ๋ํ ์ด๋ฏธ์ง, ์์๋ช , ์นดํ ๊ณ ๋ฆฌ, ์ฃผ์ ๋ฑ์ ์ ๋ณด๋ฅผ ๊ฐ๋จํ ํ์ํ๊ณ , ์๋์ ๊ฐ์ ๊ณต์ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
๋งํฌ ๋ณต์ฌ ๋ฐ SNS ๊ณต์ ๊ธฐ๋ฅ
- ๋งํฌ ๋ณต์ฌ:
navigator.clipboard.writeText()
ํด๋ฆฝ๋ณด๋ API๋ฅผ ์ฌ์ฉํด ํ์ฌ ํ์ด์ง URL์ ๋ณต์ฌํ๊ณ ์ฑ๊ณต/์คํจ ์ฌ๋ถ์ ๋ฐ๋ผreact-hot-toast
๋ฅผ ํตํด ํผ๋๋ฐฑ ๋ฉ์์ง๋ฅผ ํ์- ์ด๋ฉ์ผ ๊ณต์ :
mailto:
ํ๋กํ ์ฝ์ ํ์ฉํด ๊ธฐ๋ณธ ๋ฉ์ผ ์ฑ(Gmail, Apple Mail ๋ฑ)์ด ์คํ๋ ์ ์๋๋กํ์๊ณsubject
์๋ ์ด๋ฉ์ผ ์ ๋ชฉ,body
์๋ ๊ณต์ ํ ์์ URL์ด ์๋์ผ๋ก ์ฝ์ ๋๋๋ก ์ถ๊ฐ- ํธ์ํฐ ๊ณต์ :
https://www.twitter.com/intent/tweet?url=${window.location.href}
URL๋กwindow.open()
์ ์ฌ์ฉํด SNS ๊ณต์ ์ฉ ์ ํญ์ ์ด๋๋ก ๊ตฌ์ฑ- ํ์ด์ค๋ถ ๊ณต์ :
https://www.facebook.com/sharer/sharer.php?u=${window.location.href}
(ํธ์ํฐ์ ๋์ผ)