Seperator 컴포넌트 추가 및 페이지에서 사용, 스타일 수정
This commit is contained in:
parent
6f7257e944
commit
002b071c72
5 changed files with 213 additions and 118 deletions
146
src/app/page.tsx
146
src/app/page.tsx
|
|
@ -4,6 +4,7 @@ import Image from "@/profile.avif";
|
||||||
import Timeline from "@/components/TimeLine";
|
import Timeline from "@/components/TimeLine";
|
||||||
import Contact from "@/components/Contact";
|
import Contact from "@/components/Contact";
|
||||||
import Projects from "@/components/Projects";
|
import Projects from "@/components/Projects";
|
||||||
|
import Seperator from "@/components/Seperator";
|
||||||
|
|
||||||
export function Page() {
|
export function Page() {
|
||||||
const [age, setAge] = useState<number>(0);
|
const [age, setAge] = useState<number>(0);
|
||||||
|
|
@ -26,93 +27,156 @@ export function Page() {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 나이 계산
|
// 나이 계산
|
||||||
const referenceDate = new Date(2010, 10, 8); // 2010년 11월 8일 (0-indexed)
|
const referenceDate = new Date(2010, 10, 8);
|
||||||
const currentDate = new Date();
|
const currentDate = new Date();
|
||||||
let calculatedAge = currentDate.getFullYear() - referenceDate.getFullYear();
|
let calculatedAge = currentDate.getFullYear() - referenceDate.getFullYear();
|
||||||
|
|
||||||
if (currentDate < new Date(currentDate.getFullYear(), referenceDate.getMonth(), referenceDate.getDate())) {
|
if (currentDate < new Date(currentDate.getFullYear(), referenceDate.getMonth(), referenceDate.getDate())) {
|
||||||
calculatedAge -= 1;
|
calculatedAge -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
setAge(calculatedAge);
|
setAge(calculatedAge);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 블로그 데이터 가져오기
|
// 블로그 데이터 가져오기
|
||||||
fetch("https://api.imnya.ng/rss")
|
const fetchBlogData = async () => {
|
||||||
.then(response => response.json())
|
try {
|
||||||
.then(data => {
|
const response = await fetch("https://api.imnya.ng/rss");
|
||||||
if (data) setPost(data[0] || {});
|
const data = await response.json();
|
||||||
})
|
if (data) {
|
||||||
.catch(error => console.error("Error fetching posts:", error));
|
setPost(data[0] || {});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching posts:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchBlogData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col justify-center font-medium">
|
<div className="flex flex-col justify-center font-medium">
|
||||||
<div className="max-w-3xl px-4 mx-auto pt-24 pb-12 leading-8">
|
<div className="max-w-3xl px-4 mx-auto pt-24 pb-12 leading-8">
|
||||||
<h1 className="mb-4"><a href="mailto:me@imnya.ng" className="text-5xl font-medium font-serif font-ntype">me@imnya.ng</a></h1>
|
<header className="mb-6">
|
||||||
|
<h1 className="mb-4">
|
||||||
|
<a
|
||||||
|
href="mailto:me@imnya.ng"
|
||||||
|
className="text-5xl font-medium font-serif font-ntype hover:opacity-80 transition-opacity"
|
||||||
|
>
|
||||||
|
me@imnya.ng
|
||||||
|
</a>
|
||||||
|
</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section className="mb-8 space-y-4">
|
||||||
<p className="font-medium">
|
<p className="font-medium">
|
||||||
항상 <span className="font-extrabold">새로운 것</span>을 찾고 삶을 더 <span className="font-extrabold">간단명료</span>하게 만들고 있는 학생 개발자 <span className="font-extrabold">남현석</span>입니다.
|
항상 <span className="font-extrabold text-pink-600">새로운 것</span>을 찾고 삶을 더{" "}
|
||||||
|
<span className="font-extrabold text-blue-600">간단명료</span>하게 만들고 있는 학생 개발자{" "}
|
||||||
|
<span className="font-extrabold text-purple-600">남현석</span>입니다.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className="font-medium">
|
<p className="font-medium">
|
||||||
만든 것들은{" "}
|
만든 것들은{" "}
|
||||||
<a className="link-pink" target="_blank" href="https://github.com/team-neko/two_hearts" title="Chrome New Tab Extension">
|
<a
|
||||||
|
className="link-pink"
|
||||||
|
target="_blank"
|
||||||
|
href="https://github.com/team-neko/two_hearts"
|
||||||
|
title="Chrome New Tab Extension"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
Two Hearts
|
Two Hearts
|
||||||
</a>,{" "}
|
</a>
|
||||||
<a className="link-amber" target="_blank" href="https://instagram.com/today.isangjeong" title="Post meal in Instagram">
|
,{" "}
|
||||||
|
<a
|
||||||
|
className="link-amber"
|
||||||
|
target="_blank"
|
||||||
|
href="https://instagram.com/today.isangjeong"
|
||||||
|
title="Post meal in Instagram"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
오늘 인천상정중학교
|
오늘 인천상정중학교
|
||||||
</a>,{" "}
|
</a>
|
||||||
<a className="link-emerald" target="_blank" href="https://github.com/team-neko/dynamic-kawaii" title="Dark Pink VSCode Theme">
|
,{" "}
|
||||||
|
<a
|
||||||
|
className="link-emerald"
|
||||||
|
target="_blank"
|
||||||
|
href="https://github.com/team-neko/dynamic-kawaii"
|
||||||
|
title="Dark Pink VSCode Theme"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
Dynamic Kawaii
|
Dynamic Kawaii
|
||||||
</a> 이런 것들이 있습니다.
|
</a>{" "}
|
||||||
|
이런 것들이 있습니다.
|
||||||
</p>
|
</p>
|
||||||
<picture className="block bg-gray-100 my-4 rounded-xl aspect-3-2 overflow-hidden image-scale object-shadowed">
|
</section>
|
||||||
<img src={Image} className="w-full aspect-3-2 object-cover object-center" />
|
|
||||||
|
<figure className="my-8">
|
||||||
|
<picture className="block bg-gray-100 rounded-xl aspect-3-2 overflow-hidden image-scale object-shadowed">
|
||||||
|
<img
|
||||||
|
src={Image}
|
||||||
|
className="w-full aspect-3-2 object-cover object-center transition-transform duration-300 hover:scale-105"
|
||||||
|
alt="프로필 이미지"
|
||||||
|
/>
|
||||||
</picture>
|
</picture>
|
||||||
<p className="mt-6 font-medium">
|
</figure>
|
||||||
{age}살의 어린 나이지만, 저는 항상 <span className="font-extrabold">최적의 코드</span>를 목표로 하며,<br />
|
|
||||||
<span className="font-extrabold">사용자 경험</span>을 중심적으로 고민합니다.<br />
|
<section className="mb-8 space-y-4">
|
||||||
또한 <span className="font-extrabold">새로운 기술</span>에 대한 관심이 높습니다.
|
<p className="font-medium">
|
||||||
</p>
|
{age}살의 어린 나이지만, 저는 항상{" "}
|
||||||
<p className="mt-2 font-medium">
|
<span className="font-extrabold text-green-600">최적의 코드</span>를 목표로 하며,
|
||||||
초등학교 시절 <span className="font-extrabold">운영체제</span>에 흥미를 느껴 컴퓨터를 시작했고,
|
<br />
|
||||||
이후 <span className="font-extrabold">프로그래밍</span>에 관심을 갖게 되었습니다.<br />
|
<span className="font-extrabold text-indigo-600">사용자 경험</span>을 중심적으로 고민합니다.
|
||||||
초등학교 4학년 때 <span className="font-extrabold">Python</span>으로 프로그래밍을 시작했으며,
|
<br />
|
||||||
현재는 <span className="font-extrabold">TypeScript</span>를 주로 사용합니다.<br />
|
또한 <span className="font-extrabold text-orange-600">새로운 기술</span>에 대한 관심이 높습니다.
|
||||||
최근에는 정보보안 분야 중 <span className="font-extrabold">웹 해킹</span>에 관심이 많습니다.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="flex flex-row items-center justify-center p-4 bg-muted rounded-xl shadow-md mt-6">
|
<p className="font-medium">
|
||||||
|
초등학교 시절 <span className="font-extrabold text-red-600">운영체제</span>에 흥미를 느껴 컴퓨터를 시작했고,
|
||||||
|
이후 <span className="font-extrabold text-cyan-600">프로그래밍</span>에 관심을 갖게 되었습니다.
|
||||||
|
<br />
|
||||||
|
초등학교 4학년 때 <span className="font-extrabold text-yellow-600">Python</span>으로 프로그래밍을 시작했으며,
|
||||||
|
현재는 <span className="font-extrabold text-blue-600">TypeScript</span>를 주로 사용합니다.
|
||||||
|
<br />
|
||||||
|
최근에는 정보보안 분야 중 <span className="font-extrabold text-purple-600">웹 해킹</span>에 관심이 많습니다.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="my-8">
|
||||||
|
<div className="flex flex-row items-center justify-center p-6 bg-muted rounded-xl shadow-lg">
|
||||||
<img
|
<img
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
decoding="async"
|
decoding="async"
|
||||||
src="https://skillicons.dev/icons?i=typescript,js,c,cpp,rust,go,java,kotlin,py,html,css,php,react,remix,nextjs,tailwind,tauri,bun,elysia,mongodb,postgres,sqlite,docker,nginx,github,githubactions,git,arduino,raspberrypi,bots"
|
src="https://skillicons.dev/icons?i=typescript,js,c,cpp,rust,go,java,kotlin,py,html,css,php,react,remix,nextjs,tailwind,tauri,bun,elysia,mongodb,postgres,sqlite,docker,nginx,github,githubactions,git,arduino,raspberrypi,bots"
|
||||||
className="w-full max-w-3xl rounded-lg object-contain select-none pointer-events-none transition-transform duration-200 hover:scale-105"
|
className="w-full max-w-3xl rounded-lg object-contain select-none pointer-events-none transition-transform duration-300 hover:scale-105"
|
||||||
alt="기술 스택"
|
alt="기술 스택"
|
||||||
title="기술 스택"
|
title="기술 스택"
|
||||||
width={800}
|
width={800}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<br />
|
{post.title && (
|
||||||
|
<section className="mb-8">
|
||||||
<p className="font-medium">
|
<p className="font-medium">
|
||||||
최근 블로그 글:{" "}
|
최근 블로그 글:{" "}
|
||||||
<a href={post.link} className="text-muted-foreground">
|
<a
|
||||||
|
href={post.link}
|
||||||
|
className="text-muted-foreground hover:text-foreground transition-colors underline decoration-dashed underline-offset-4"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
{post.title}
|
{post.title}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="border-t-1 border-muted rounded-full my-4" />
|
<Seperator />
|
||||||
|
|
||||||
<Projects />
|
<Projects />
|
||||||
|
<Seperator />
|
||||||
<div className="border-t-1 border-muted rounded-full mt-8" />
|
|
||||||
|
|
||||||
|
|
||||||
<Timeline />
|
<Timeline />
|
||||||
<div className="border-t-1 border-muted rounded-full mt-8" />
|
<Seperator />
|
||||||
|
|
||||||
<Contact />
|
<Contact />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ const projects = [
|
||||||
|
|
||||||
export default function Projects() {
|
export default function Projects() {
|
||||||
return (
|
return (
|
||||||
<div id="projects">
|
<div id="projects" className="mt-8">
|
||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
{projects.map((project, index) => (
|
{projects.map((project, index) => (
|
||||||
<ProjectsComponents key={index} project={project} />
|
<ProjectsComponents key={index} project={project} />
|
||||||
|
|
|
||||||
5
src/components/Seperator.tsx
Normal file
5
src/components/Seperator.tsx
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
export default function Seperator() {
|
||||||
|
return (
|
||||||
|
<div className="border-t-1 border-muted rounded-full mt-8" />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+KR&family=Noto+Color+Emoji&display=swap');
|
/*@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+KR&family=Noto+Color+Emoji&display=swap');*/
|
||||||
|
@import url("https://cdn.jsdelivr.net/gh/wanteddev/wanted-sans@v1.0.3/packages/wanted-sans/fonts/webfonts/variable/split/WantedSansVariable.min.css");
|
||||||
@import "../styles/globals.css";
|
@import "../styles/globals.css";
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
|
|
@ -17,7 +18,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
font-family: 'IBM Plex Sans KR', sans-serif;
|
/*font-family: 'IBM Plex Sans KR', sans-serif;*/
|
||||||
|
font-family: "Wanted Sans Variable", "Wanted Sans", -apple-system, BlinkMacSystemFont, system-ui, "Segoe UI", "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.font-ntype {
|
.font-ntype {
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,34 @@ import index from "./index.html";
|
||||||
|
|
||||||
// Parse command line arguments for port
|
// Parse command line arguments for port
|
||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
const portArgIndex = args.findIndex(arg => arg === "--port");
|
const portArgIndex = args.findIndex((arg) => arg === "--port");
|
||||||
const port = portArgIndex !== -1 && args[portArgIndex + 1] ?
|
const defaultPort =
|
||||||
parseInt(args[portArgIndex + 1]) : 3000;
|
portArgIndex !== -1 && args[portArgIndex + 1]
|
||||||
|
? parseInt(args[portArgIndex + 1])
|
||||||
|
: 3000;
|
||||||
|
|
||||||
|
// Function to find available port
|
||||||
|
async function findAvailablePort(startPort: number): Promise<number> {
|
||||||
|
let port = startPort;
|
||||||
|
while (port < 65535) {
|
||||||
|
try {
|
||||||
|
const testServer = serve({
|
||||||
|
port: port,
|
||||||
|
fetch() {
|
||||||
|
return new Response("test");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
testServer.stop();
|
||||||
|
return port;
|
||||||
|
} catch (error) {
|
||||||
|
port++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error("No available port found");
|
||||||
|
}
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const port = await findAvailablePort(defaultPort);
|
||||||
|
|
||||||
const server = serve({
|
const server = serve({
|
||||||
port: port,
|
port: port,
|
||||||
|
|
@ -20,7 +44,7 @@ const server = serve({
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "text/plain",
|
"content-type": "text/plain",
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -40,4 +64,4 @@ if (process.env.NODE_ENV !== "production") {
|
||||||
console.log(`\x1b[0m\x1b[35m Bun v${Bun.version}\x1b[0m`);
|
console.log(`\x1b[0m\x1b[35m Bun v${Bun.version}\x1b[0m`);
|
||||||
}
|
}
|
||||||
console.log(`\n\x1b[34m→ \x1b[35m${server.url}\x1b[0m`);
|
console.log(`\n\x1b[34m→ \x1b[35m${server.url}\x1b[0m`);
|
||||||
|
})();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue