From 9525f1ee53fe46e959af2b085cfdc7613b8d33e0 Mon Sep 17 00:00:00 2001
From: tolelom <98kimsungmin@naver.com>
Date: Sun, 22 Mar 2026 22:29:06 +0900
Subject: [PATCH] feat: add Docker deployment, design polish, and content
update
- Add Dockerfile, nginx.conf, docker-compose.yml for containerized deployment
- Improve typography with Pretendard font, larger hero, accent colors
- Add Experience section (HI-ARC, ICPC, contests)
- Add TOL project, enhance project descriptions
- Update Skills with Backend category, more technologies
- Add Solved.ac link, education period
- Change nav logo to "tolelom", widen content area to 960px
- Project card links now use domain labels as buttons
Co-Authored-By: Claude Opus 4.6
---
.gitignore | 1 +
Dockerfile | 4 +
css/style.css | 271 +++++++++++++++++++++++++++++++--------------
data/projects.js | 27 ++++-
docker-compose.yml | 6 +
index.html | 73 +++++++++++-
js/main.js | 13 ++-
nginx.conf | 10 ++
sitemap/index.html | 3 +-
9 files changed, 307 insertions(+), 101 deletions(-)
create mode 100644 .gitignore
create mode 100644 Dockerfile
create mode 100644 docker-compose.yml
create mode 100644 nginx.conf
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7a95436
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.superpowers/
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..de6aea6
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,4 @@
+FROM nginx:alpine
+COPY nginx.conf /etc/nginx/conf.d/default.conf
+COPY . /usr/share/nginx/html
+RUN rm /usr/share/nginx/html/Dockerfile /usr/share/nginx/html/nginx.conf /usr/share/nginx/html/docker-compose.yml 2>/dev/null || true
diff --git a/css/style.css b/css/style.css
index a455dfa..96bdadb 100644
--- a/css/style.css
+++ b/css/style.css
@@ -5,182 +5,276 @@
html { scroll-behavior: smooth; scroll-padding-top: 64px; }
body {
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+ font-family: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
font-size: 16px;
- line-height: 1.6;
- color: #333;
+ line-height: 1.7;
+ color: #2d2d2d;
background: #fff;
+ -webkit-font-smoothing: antialiased;
}
-a { color: #333; text-decoration: none; }
-a:hover { color: #0066cc; }
+a { color: #2d2d2d; text-decoration: none; }
+a:hover { color: #0055cc; }
img { max-width: 100%; display: block; }
/* === Layout === */
-.container { max-width: 800px; margin: 0 auto; padding: 0 24px; }
+.container { max-width: 960px; margin: 0 auto; padding: 0 40px; }
.section {
- padding: 64px 0;
+ padding: 72px 0;
border-bottom: 1px solid #eee;
}
.section:last-child { border-bottom: none; }
.section-title {
- font-size: 1.25rem;
+ font-size: 1.35rem;
font-weight: 700;
- margin-bottom: 24px;
+ margin-bottom: 28px;
color: #111;
+ letter-spacing: -0.02em;
}
/* === Nav === */
.nav {
position: sticky;
top: 0;
- background: rgba(255, 255, 255, 0.95);
- backdrop-filter: blur(8px);
+ background: rgba(255, 255, 255, 0.92);
+ backdrop-filter: blur(12px);
+ -webkit-backdrop-filter: blur(12px);
border-bottom: 1px solid #eee;
z-index: 100;
- padding: 0 24px;
+ padding: 0 40px;
}
.nav-inner {
- max-width: 800px;
+ max-width: 960px;
margin: 0 auto;
display: flex;
align-items: center;
height: 56px;
- gap: 24px;
+ gap: 28px;
}
.nav-logo {
- font-weight: 700;
- font-size: 1rem;
+ font-weight: 800;
+ font-size: 1.05rem;
margin-right: auto;
+ letter-spacing: -0.03em;
}
.nav-link {
font-size: 0.875rem;
- color: #666;
+ color: #888;
+ font-weight: 500;
transition: color 0.2s;
}
.nav-link:hover, .nav-link.active { color: #111; }
-.nav-external::after { content: " ↗"; font-size: 0.75rem; }
+.nav-external::after { content: " ↗"; font-size: 0.7rem; opacity: 0.6; }
/* === Hero === */
.hero {
- padding: 80px 0 64px;
+ padding: 100px 0 72px;
text-align: center;
}
-.hero-name { font-size: 2rem; font-weight: 700; color: #111; }
-.hero-role { font-size: 1.1rem; color: #666; margin-top: 8px; }
+.hero-name {
+ font-size: 3rem;
+ font-weight: 800;
+ color: #111;
+ letter-spacing: -0.04em;
+ line-height: 1.1;
+}
+
+.hero-role {
+ font-size: 1.2rem;
+ color: #555;
+ margin-top: 12px;
+ font-weight: 400;
+}
.hero-tags {
display: flex;
- gap: 8px;
+ gap: 10px;
justify-content: center;
- margin-top: 16px;
+ margin-top: 24px;
flex-wrap: wrap;
}
.tag {
- padding: 4px 12px;
- border-radius: 4px;
+ padding: 5px 14px;
+ border-radius: 6px;
font-size: 0.85rem;
- background: #f0f0f0;
- color: #555;
+ background: #f4f4f5;
+ color: #444;
+ font-weight: 500;
}
.hero-links {
display: flex;
gap: 12px;
justify-content: center;
- margin-top: 24px;
+ margin-top: 32px;
}
.btn {
- padding: 8px 20px;
- border-radius: 6px;
+ padding: 10px 24px;
+ border-radius: 8px;
font-size: 0.875rem;
+ font-weight: 500;
border: 1px solid #ddd;
background: #fff;
color: #333;
cursor: pointer;
- transition: background 0.2s, border-color 0.2s;
+ transition: all 0.2s;
text-decoration: none;
display: inline-block;
}
.btn:hover { background: #f5f5f5; border-color: #bbb; }
-.btn-primary { background: #111; color: #fff; border-color: #111; }
+.btn-primary {
+ background: #111;
+ color: #fff;
+ border-color: #111;
+}
.btn-primary:hover { background: #333; }
/* === Project Card === */
-.project-list { display: flex; flex-direction: column; gap: 16px; }
+.project-list { display: flex; flex-direction: column; gap: 20px; }
.project-card {
- padding: 20px;
- border: 1px solid #eee;
- border-radius: 8px;
- transition: border-color 0.2s;
+ padding: 24px;
+ border: 1px solid #e8e8e8;
+ border-radius: 12px;
+ transition: border-color 0.2s, box-shadow 0.2s;
+}
+.project-card:hover {
+ border-color: #d0d0d0;
+ box-shadow: 0 2px 12px rgba(0,0,0,0.04);
}
-.project-card:hover { border-color: #ccc; }
.project-card-header {
display: flex;
align-items: baseline;
gap: 12px;
- margin-bottom: 8px;
+ margin-bottom: 10px;
+}
+.project-card-name {
+ font-weight: 700;
+ font-size: 1.1rem;
+ color: #111;
+ letter-spacing: -0.02em;
}
-.project-card-name { font-weight: 600; font-size: 1.05rem; }
.project-card-category {
font-size: 0.75rem;
- color: #999;
+ color: #0055cc;
+ font-weight: 600;
+ background: #eef4ff;
+ padding: 2px 8px;
+ border-radius: 4px;
+}
+.project-card-desc {
+ color: #555;
+ font-size: 0.95rem;
+ margin-bottom: 14px;
+ line-height: 1.6;
}
-.project-card-desc { color: #666; font-size: 0.925rem; margin-bottom: 12px; }
.project-card-tags { display: flex; gap: 6px; flex-wrap: wrap; }
.project-card-tag {
- padding: 2px 8px;
- background: #f5f5f5;
- border-radius: 3px;
+ padding: 3px 10px;
+ background: #f4f4f5;
+ border-radius: 4px;
font-size: 0.8rem;
- color: #666;
+ color: #555;
+ font-weight: 500;
}
-.project-card-link {
- display: inline-block;
- margin-top: 12px;
- font-size: 0.85rem;
- color: #0066cc;
+.project-card-links {
+ display: flex;
+ gap: 8px;
+ margin-top: 16px;
+ flex-wrap: wrap;
}
-.project-card-link:hover { text-decoration: underline; }
+
+.btn-sm {
+ padding: 6px 14px;
+ border-radius: 6px;
+ font-size: 0.8rem;
+ font-weight: 500;
+ border: 1px solid #ddd;
+ background: #fff;
+ color: #333;
+ text-decoration: none;
+ display: inline-block;
+ transition: all 0.2s;
+}
+.btn-sm:hover { background: #f5f5f5; border-color: #bbb; color: #111; }
/* === Skills === */
-.skills-group { margin-bottom: 20px; }
+.skills-group { margin-bottom: 24px; }
.skills-group:last-child { margin-bottom: 0; }
.skills-group-title {
- font-size: 0.8rem;
+ font-size: 0.75rem;
text-transform: uppercase;
- letter-spacing: 1px;
+ letter-spacing: 1.5px;
color: #999;
- margin-bottom: 8px;
+ margin-bottom: 10px;
+ font-weight: 600;
}
.skills-tags { display: flex; gap: 8px; flex-wrap: wrap; }
+/* === Experience === */
+.experience-list { display: flex; flex-direction: column; gap: 16px; }
+
+.experience-item {
+ padding: 16px 20px;
+ border-left: 3px solid #e0e0e0;
+ transition: border-color 0.2s;
+}
+.experience-item:hover { border-left-color: #0055cc; }
+
+.experience-header {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+ gap: 12px;
+ margin-bottom: 4px;
+}
+
+.experience-title {
+ font-weight: 600;
+ font-size: 1rem;
+ color: #111;
+}
+
+.experience-period {
+ font-size: 0.8rem;
+ color: #999;
+ white-space: nowrap;
+}
+
+.experience-desc {
+ color: #666;
+ font-size: 0.9rem;
+}
+
/* === Sitemap Cards === */
.sitemap-header {
text-align: center;
- padding: 48px 0 32px;
+ padding: 56px 0 36px;
}
-.sitemap-title { font-size: 1.5rem; font-weight: 700; }
-.sitemap-subtitle { color: #999; margin-top: 4px; }
+.sitemap-title {
+ font-size: 1.75rem;
+ font-weight: 800;
+ letter-spacing: -0.03em;
+}
+.sitemap-subtitle { color: #888; margin-top: 6px; font-size: 0.95rem; }
.sitemap-section-title {
- font-size: 0.9rem;
+ font-size: 0.8rem;
text-transform: uppercase;
- letter-spacing: 1px;
+ letter-spacing: 1.5px;
color: #999;
font-weight: 600;
margin-bottom: 16px;
@@ -189,14 +283,14 @@ img { max-width: 100%; display: block; }
.card-grid {
display: grid;
grid-template-columns: 1fr 1fr;
- gap: 12px;
- margin-bottom: 40px;
+ gap: 14px;
+ margin-bottom: 48px;
}
.site-card {
- border: 1px solid #eee;
- padding: 24px;
- border-radius: 10px;
+ border: 1px solid #e8e8e8;
+ padding: 28px;
+ border-radius: 12px;
text-align: center;
text-decoration: none;
color: inherit;
@@ -204,18 +298,19 @@ img { max-width: 100%; display: block; }
display: block;
}
.site-card:hover {
- border-color: #ccc;
- box-shadow: 0 2px 8px rgba(0,0,0,0.04);
+ border-color: #d0d0d0;
+ box-shadow: 0 4px 16px rgba(0,0,0,0.06);
}
-.site-card-icon { font-size: 1.5rem; margin-bottom: 8px; }
-.site-card-name { font-weight: 600; font-size: 0.95rem; }
-.site-card-desc { color: #999; font-size: 0.8rem; margin-top: 4px; }
+.site-card-icon { font-size: 1.75rem; margin-bottom: 10px; }
+.site-card-name { font-weight: 700; font-size: 1rem; color: #111; }
+.site-card-desc { color: #888; font-size: 0.85rem; margin-top: 6px; }
.site-card-sublink {
display: inline-block;
margin-top: 8px;
font-size: 0.75rem;
- color: #0066cc;
+ color: #0055cc;
+ font-weight: 500;
}
/* Wrapper to keep card + sublink in one grid cell */
@@ -224,28 +319,31 @@ img { max-width: 100%; display: block; }
.infra-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
- gap: 10px;
+ gap: 12px;
}
.infra-card {
- border: 1px solid #eee;
- padding: 16px;
- border-radius: 8px;
+ border: 1px solid #e8e8e8;
+ padding: 20px;
+ border-radius: 10px;
text-align: center;
text-decoration: none;
color: #666;
background: #fafafa;
- transition: border-color 0.2s;
+ transition: border-color 0.2s, box-shadow 0.2s;
display: block;
}
-.infra-card:hover { border-color: #ccc; }
-.infra-card-icon { font-size: 1.2rem; }
-.infra-card-name { font-size: 0.85rem; margin-top: 4px; }
-.infra-card-desc { font-size: 0.7rem; color: #aaa; margin-top: 2px; }
+.infra-card:hover {
+ border-color: #d0d0d0;
+ box-shadow: 0 2px 8px rgba(0,0,0,0.04);
+}
+.infra-card-icon { font-size: 1.3rem; }
+.infra-card-name { font-size: 0.85rem; margin-top: 6px; font-weight: 600; color: #444; }
+.infra-card-desc { font-size: 0.72rem; color: #aaa; margin-top: 3px; }
/* === Footer === */
.footer {
- padding: 32px 0;
+ padding: 40px 0;
text-align: center;
color: #999;
font-size: 0.85rem;
@@ -255,11 +353,14 @@ img { max-width: 100%; display: block; }
/* === Responsive === */
@media (max-width: 640px) {
+ .container { padding: 0 20px; }
+ .nav { padding: 0 20px; }
.nav-inner { gap: 16px; }
.nav-link { font-size: 0.8rem; }
- .hero { padding: 48px 0 40px; }
- .hero-name { font-size: 1.5rem; }
- .section { padding: 40px 0; }
+ .hero { padding: 60px 0 48px; }
+ .hero-name { font-size: 2rem; }
+ .hero-role { font-size: 1rem; }
+ .section { padding: 48px 0; }
.card-grid { grid-template-columns: 1fr; }
.infra-grid { grid-template-columns: 1fr 1fr; }
}
diff --git a/data/projects.js b/data/projects.js
index ae4d26a..2977168 100644
--- a/data/projects.js
+++ b/data/projects.js
@@ -3,28 +3,43 @@ const projects = [
name: "One of the Plans (A301)",
description: "Unity 기반 MMORPG 게임 프로젝트. 멀티플레이어 서버는 Go로 구현.",
tags: ["Unity", "C#", "Go"],
- link: "https://a301.tolelom.xyz",
+ links: [
+ { label: "a301.tolelom.xyz", url: "https://a301.tolelom.xyz" },
+ { label: "GitHub", url: "https://github.com/tolelom" }
+ ],
category: "게임"
},
{
name: "No-Ill",
- description: "노인 낙상 감지 및 외로움 방지를 위한 IoT 장치와 보호자 앱.",
+ description: "노인 낙상 감지 및 외로움 방지를 위한 IoT 장치와 보호자 앱. 센서 기반 낙상 감지 시스템과 보호자용 모니터링 웹/앱을 개발.",
tags: ["React", "Go", "IoT"],
- link: "https://no-ill.tolelom.xyz",
+ links: [
+ { label: "no-ill.tolelom.xyz", url: "https://no-ill.tolelom.xyz" },
+ { label: "no-ill-app.tolelom.xyz", url: "https://no-ill-app.tolelom.xyz" }
+ ],
category: "헬스케어"
},
+ {
+ name: "TOL",
+ description: "한글 프로그래밍 언어 개발 프로젝트. 3인 팀으로 진행하며 프로그래밍 언어 분야에 흥미를 가지게 된 계기. 순수 C++로 언어를 설계하고 구현.",
+ tags: ["C++", "Express.js", "MongoDB"],
+ links: [],
+ category: "언어 개발"
+ },
{
name: "SION",
description: "Jetson 키트를 활용한 자율주행 및 모니터링 웹 페이지.",
tags: ["Python", "Jetson", "Web"],
- link: "https://sion.tolelom.xyz",
+ links: [
+ { label: "sion.tolelom.xyz", url: "https://sion.tolelom.xyz" }
+ ],
category: "IoT"
},
{
name: "Neo-Slasher",
- description: "Unity 기반 액션 게임 프로젝트.",
+ description: "게임 개발 동아리 ExP에서 8인 팀으로 진행한 액션 게임. 프로그래밍 디렉터로 데이터 관리, 인터페이스 개발 및 전체 프로그래밍 과정을 담당.",
tags: ["Unity", "C#"],
- link: null,
+ links: [],
category: "게임"
}
];
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..31f18e4
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,6 @@
+services:
+ web:
+ build: .
+ restart: unless-stopped
+ ports:
+ - "8080:80"
diff --git a/index.html b/index.html
index 79af005..1ed982e 100644
--- a/index.html
+++ b/index.html
@@ -10,14 +10,16 @@
+
diff --git a/js/main.js b/js/main.js
index f907d13..d6968ed 100644
--- a/js/main.js
+++ b/js/main.js
@@ -7,9 +7,14 @@ function renderProjects() {
return '' + t + "";
}).join("");
- var link = p.link
- ? '사이트 방문 →'
- : "";
+ var links = "";
+ if (p.links && p.links.length > 0) {
+ links = '' +
+ p.links.map(function (l) {
+ return '
' + l.label + '';
+ }).join("") +
+ "
";
+ }
return (
'' +
@@ -19,7 +24,7 @@ function renderProjects() {
"
" +
'' + p.description + "
" +
'' + tags + "
" +
- link +
+ links +
""
);
}).join("");
diff --git a/nginx.conf b/nginx.conf
new file mode 100644
index 0000000..8565b53
--- /dev/null
+++ b/nginx.conf
@@ -0,0 +1,10 @@
+server {
+ listen 80;
+ server_name localhost;
+ root /usr/share/nginx/html;
+ index index.html;
+
+ location / {
+ try_files $uri $uri/ =404;
+ }
+}
diff --git a/sitemap/index.html b/sitemap/index.html
index 0448157..ceeb868 100644
--- a/sitemap/index.html
+++ b/sitemap/index.html
@@ -10,12 +10,13 @@
+