box-shadow๋ก ์ฃผ๋ณ์ ๋ฐํ๋ค ์ด๋ก๊ฒ ํ๋ค ๋ฐ๋ณตํ๋ ๊ฒ์ผ๋ก ๋ฐ์ง์ด๋ ๊ฒ์ฒ๋ผ ์ฐ์ถํ ๋ฐฉ์์ ๋ณด๊ณ ์ฐธ๊ณ ํด์ ์์ฑํ๋ค.
ํด๋์ค๋ช ์ผ๋ก ์ฌ์ฉํ ๋ฐฐ์ด๋ก ๊ฐ๊ฐ ๋ณ์ ์ฌ์ด์ฆ, ํฌ๋ช ๋, **์ ๋๋ฉ์ด์ ** (์๋์์ ๋ด๊ฐ ์ ์ํด์ ์ฌ์ฉํ ๊ฒ, ์ด ํด๋์ค๋ช ์์ ์ด์๊ฐ ์์๋ค.... ํ๋จ์์ ์ค๋ช ํ ๊ฒ)์ ์ ์ธํ๊ณ
getRandomValue ํจ์๋ก ๋ณ์ด ๋ฟ๋ ค์ง ๋๋คํ ๊ฐฏ์๋ฅผ ๋ฆฌํดํ๋ค.
ํ์ด์ง์ ๊ธฐ๋ณธ ๋ฐฐ๊ฒฝ์ผ๋ก ์ฌ์ฉํ ์์ ์ด์๊ธฐ์ ์ฌ์ฉ์๊ฐ ์ฐ๋ ๋ธ๋ผ์ฐ์ ์ฐฝ์ ํฌ๊ธฐ๋ฅผ ๊ธฐ์ค์ผ๋ก ๋ณ์ ๋ฟ๋ ค์ฃผ๋ ค๊ณ ํ๋๋ฐ, window๋ฅผ ์ฐธ์กฐํ๋ ค๊ณ ํ๋ window is not defined ์๋ฌ๊ฐ ๋ด๋ค. ์ฐพ์๋ณด๋ nextjs๊ฐ ssr๋ก ์๋ฒ์์ ๋จผ์ HTML์ ๊ทธ๋ ค๋๋๋ฐ ํด๋ผ์ด์ธํธ๊ฐ window์ ์ ๊ทผํ๊ธฐ ์ ์ ๊ทธ๋ ค์ง๊ธฐ ๋๋ฌธ์ ์ฐธ์กฐ ์๋ฌ๊ฐ ๋์ค๋ ๊ฒ !!
ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์ฐพ์๋ณด๋
1) ํ์ ๋ถ๊ธฐ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ๊ฑฐ๋, 2) useEffect๋ฅผ ์ฌ์ฉํ๊ฑฐ๋, ์๋๋ฉด ๋ค์์ฒ๋ผ 3) ssr์ ๋นํ์ฑํ์ํค๋ ์ต์ ์ด ์์๋ค.
// ssr ๋นํ์ฑํ
'use client'
import dynamic from 'next/dynamic'
const DynamicHeader = dynamic(() => import('../components/header'), {
ssr: false,
})
๊ทธ ์ค์ ๋๋ useEffect๋ก ์์ฑํ๋ฉด์ ๋ด๋ถ์ ํ์ ๋ถ๊ธฐ ์ฒ๋ฆฌํด์ฃผ๋ ๋ฐฉ์์ผ๋ก 2์ค ์ฒ๋ฆฌํ๋ค.
useEffect๋ฅผ ์ฌ์ฉํด์ ๋ด๋ถ์ ์์ฑํ๋ฉด ๋ธ๋ผ์ฐ์ ๊ฐ ๋ ๋๋ง๋๋ ์์ ์ ์ฝ๋๋ฅผ ์คํ์ํค๊ธฐ ๋๋ฌธ์ ssr ํ๊ฒฝ์ด์ด๋ ์๋์ด ๋๋ค.
์ต์ข ์ผ๋ก ์์ฑํ ๋ฐฐ๊ฒฝ ์ปดํฌ๋ํธ
"use client";
import { useState, useEffect } from "react";
export default function StarrySky() {
const [stars, setStars] = useState([]);
const style = ["w-[1px] h-[1px]", "w-[1.5px] h-[1.5px]", "w-[2px] h-[2px]"];
const opacity = ["opacity-50", "opacity-100", "opacity-10", "opacity-50", "opacity-10"];
const twinkle = ["animate-twinkle1", "animate-twinkle2", "animate-twinkle3", "animate-twinkle4"];
const getRandomValue = (maxNum) => {
return Math.floor(Math.random() * maxNum);
};
const maxNum = 300; // maxNum ์์๋ก ๋ฃ์๋๋ฐ ๋ณ๊ฒฝ ๊ฐ๋ฅ
// ** useEffect => nextjs์ window ๊ฐ์ฒด ์ฐธ์กฐ ์ค๋ฅ!! ๊ด๋ จํด์ window ์ฌ์ฉํ๊ธฐ ์ํด useEffect๋ก ๋ฌถ๊ณ ๊ทธ ์์์ ์ฌ์ฉ
useEffect(() => {
if (typeof window !== "undefined") {
const browserWidth = window.screen.width;
const browserHeight = window.screen.height;
let starArr = [];
for (let i = 0; i < maxNum; i++) {
const x = getRandomValue(browserWidth);
const y = getRandomValue(browserHeight);
const starSize = style[getRandomValue(style.length)];
const starOpacity = opacity[getRandomValue(opacity.length)];
const starTwinkle = twinkle[getRandomValue(twinkle.length)];
starArr.push({
id: i,
x,
y,
starSize,
starOpacity,
starTwinkle,
});
}
setStars(starArr);
}
},
[]);
return (
<div className="absolute inset-0 overflow-hidden">
{stars.map((star) => (
<div
key={star.id}
className={`absolute rounded-full bg-white ${star.starSize} ${star.starOpacity} ${star.starTwinkle}`}
style={{ left: star.x, top: star.y }}
></div>
))}
</div>
);
}
๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ์ ์ ์ ์ ๋๋ฉ์ด์ ์ ์์ฑํ๊ธฐ ์ํด์ tailwind.config.js ํ์ผ์ ๋ค์๊ณผ ๊ฐ์ ์ต์ ์ ์ถ๊ฐํ๋ค.
// tailwind.config.js
module.exports = {
theme: {
extend: {
animation: {
twinkle1: "twinkling 0.5s infinite",
twinkle2: "twinkling 1.1s infinite",
twinkle3: "twinkling 1.3s infinite",
twinkle4: "twinklingWithoutBoxShadow 3s infinite",
},
keyframes: {
twinkling: {
"0%, 100%": {
boxShadow: "0 0 10px 0px rgba(255, 255, 255, 0.1)",
},
"50%": {
boxShadow: "0 0 10px 2px rgba(255, 255, 255, 0.6)",
},
},
twinklingWithoutBoxShadow: {
"0%": {
backgroundColor: "#ffffff",
boxShadow: "0 0 10px 0px rgba(255, 255, 255, 1)",
},
"20%": {
backgroundColor: "#ffc4c4",
boxShadow: "0 0 10px 0px rgba(255, 196, 196, 1)",
},
"80%": {
backgroundColor: "#c4cfff",
boxShadow: "0 0 10px 0px rgba(196, 207, 255, 1)",
},
"100%": {
backgroundColor: "#ffffff",
boxShadow: "0 0 10px 0px rgba(255, 255, 255, 0.2)",
},
},
},
},
},
plugins: [],
};
animation์๋ keyframes์ ์์ฑํ ์ ๋๋ฉ์ด์ ์ด๋ฆ์ ์ ์ด์ฃผ๊ณ , css์์ ์์ฑ์ ์ฌ์ฉํ๋ ๊ฒ์ฒ๋ผ ์์ฑํ๋ฉด ๋๋ค. ๊ทธ๋ฆฌ๊ณ keyframes์๋ ์ ๋๋ฉ์ด์ ์ ์ด๋ฆ๊ณผ ํจ๊ณผ๋ฅผ ๋ฃ์ด์ค๋ค. ์์ฒ๋ผ 0%, 50%, 100%๋ก ํผ์ผํฐ์ง๋ก ๋๋ ์ ์์ฑํ ์๋ ์๋๋ฐ ๊ทธ๋ผ๋ฐ์ด์ ๊ตฌํํ์ ๋์ฒ๋ผ from, to๋ก ์จ๋ ๋๋ ๊ฒ ๊ฐ๋ค.
rgb์ rgba์ ์ฐจ์ด๋ ์ด๋ฒ์ ๋ฐฐ์ ๋ค. ํฌ๋ช ๋๋ฅผ ์ค๊ฑฐ๋ฉด a๋ฅผ ๋ถ์ฌ์ผํจ~!!! rgb + alpha(ํฌ๋ช ๋)์ธ ๊ฒ์ด๋ค
alpha ๊ฐ์ 0~1๊น์ง์ ๊ฐ์ ๊ฐ์ง๊ณ , 0์ ๊ฐ๊น์ธ ์๋ก ํฌ๋ช ํ๋ค.
์ฌ๊ธฐ๊น์ง ์์ฑํ์ผ๋ฉด ์ ๋ ๊ฒ์ด๋ผ๊ณ ์์ํ๊ณ dev ์๋ฒ๋ฅผ ์คํ์์ผฐ๋๋ฐ boxShadow ํจ๊ณผ๊ฐ ์์์ฒ๋ผ ์คํ์ด ๋์ง ์์๋ค.
// ์ฒ์์ ๋์ ์ ๋๋ ์ฝ๋
const twinkle = ["twinkle1", "twinkle2", "twinkle3", "twinkle4"];
// ๊ณ ์น๊ณ ๋์๋๋ ์ฝ๋
const twinkle = ["animate-twinkle1", "animate-twinkle2", "animate-twinkle3", "animate-twinkle4"];
animation์์ ์์ฑํ ๊ทธ๋๋ก ์ปดํฌ๋ํธ์ ํด๋์ค๋ช ์ผ๋ก ์ฌ์ฉํ๋ฉด ๋์์ด ์๋๋ค !!!
๋ฐ๋์ 'animation-' ์ ์ ๋์ฌ๋ก ๋ถ์ฌ์ ์ปดํฌ๋ํธ์ ํด๋์ค๋ช ์ผ๋ก ์ฌ์ฉํด์ผ ์ฌ์ฉ์๊ฐ ์ ์ํ ์ ๋๋ฉ์ด์ ์ด ๋์๋๋ค. (ใ .,ใ )
์ด๋ ๊ฒ ํ๊ณ root์ layout ํ์ผ์ ๋ฃ์ด์ฃผ๋ฉด ๋ชจ๋ ํ์ด์ง์ ์ ์ฉ์ด ๋๋ค.
์ฐธ๊ณ ๋ก ๋ฐฐ๊ฒฝ์ ๊ทธ๋ผ๋ฐ์ด์ ์ปฌ๋ฌ๋ globals.css > body > background์ ์ปฌ๋ฌ๋ฅผ ์ง์ ํด์คฌ๋ค.
ํธ๋ฌ๋ธ ์ํ !!
์์ฒ๋ผ ๋ฐฑ๊ทธ๋ผ์ด๋๋ฅผ ๋ง๋ค์ด์ฃผ๊ณ ๋ค๋ฅธ ๊ธฐ๋ฅ ๊ฐ๋ฐ์ ํ๋ ค๊ณ ์์๋ก ๋ฒํผ์ ๋ง๋ค์๋๋ฐ, ๋ฒํผ ํด๋ฆญ ์ด๋ฒคํธ์กฐ์ฐจ ๋์ง ์์๋ค.
๋ญ๊ฐ ๋ฌธ์ ์ธ์ง ๋ชฐ๋์๋๋ฐ ์ฌ๊ธฐ์ ํด๊ฒฐํ๋ค.
export default async function RootLayout({ children }) {
const session = await getServerSession();
return (
<html lang="en">
<body className={inter.className}>
<Providers session={session}>
<StarrySky /> // ์ฌ๊ธฐ๊ฐ ๋ง๋ ์์น
{children}
// <StarrySky /> ๋ฌธ์ ๊ฐ ์๊ธด ์ด์ .
</Providers>
</body>
</html>
);
}
์ฐ์ ์์์ ๋ฐ๋ ค์ ๋ค๋ฅธ ๋ ์๋ค์ด ๊น๋ฆฐ ๊ฑฐ ์๋ค !!! (ใ .,ใ ) ์ ์ญ์ผ๋ก ์ค์ ํด์ฃผ๋ ค๋ฉด {children}์ ์๋จ์ ์์ด์ผ ํ๋ค..
'What I Learn' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
OSSCA : setting the Continue at vscode & Overview (0) | 2024.09.23 |
---|---|
์ ์ญ์์ ๋ชจ๋ฌ ์ํ๊ด๋ฆฌํ๊ธฐ (redux-toolkit) (1) | 2024.09.20 |
Make Calendar without Calendar library (@date-fns) (0) | 2024.09.13 |
Separate Next.js Root Layout from 404 Page Layout (0) | 2024.09.01 |
picture, source, img ํ๊ทธ๋ฅผ ์ด์ฉํ ๋ฐ์ํ ์ด๋ฏธ์ง ๊ตฌํ (CSS) (0) | 2024.08.25 |