본문으로 건너뛰기
← 블로그로 돌아가기
개발

사이트 UX 대규모 업그레이드공유 버튼, 뉴스레터, 디자인 개선 기록

6분 읽기
#UX#Next.js#Vercel#Newsletter#Buttondown#Design

오늘 하루 동안 White Place와 Dark Place에 꽤 많은 기능을 추가했다. Claude Code와 함께 작업하면서 계획 수립부터 구현, 배포, 디자인 리뷰까지 한 사이클을 돌렸는데, 그 과정을 기록으로 남긴다.

무엇을 했나

크게 4가지 카테고리로 나눠서 작업했다.

Tier 1 — Quick Wins: 공유 버튼, White Place ↔ Dark Place 상호 내비게이션, Sitemap 날짜 수정, AI 뉴스 네비바 노출. 각각 10~20분 내로 끝나는 작업들이었다.

Tier 2 — Medium Effort: Sitemap lastModified에 실제 포스트 날짜 반영 등.

Tier 3 — Larger Features: Buttondown API 연동 뉴스레터, Playground 통합 리더보드, Dark Place 랩 진행도 추적.

디자인 리뷰: 처음 구현한 디자인이 마음에 안 들어서 두 번 리디자인했다. 결국 깔끔한 카드 스타일로 정착.

공유 버튼 추가

블로그 글을 읽고 나서 공유할 수단이 없다는 게 가장 큰 문제였다. Twitter/X, LinkedIn, Instagram, 링크 복사 4개 버튼을 만들었다.

처음에는 네모난 박스 형태(rounded-lg)였는데, 원형(rounded-full)으로 바꾸니까 훨씬 깔끔해졌다. hover 시 accent 컬러로 채워지는 효과도 추가했다.

const btnClass =
  "inline-flex items-center justify-center w-9 h-9 rounded-full " +
  "bg-[var(--bg-secondary)] hover:bg-[var(--accent)] " +
  "text-[var(--text-tertiary)] hover:text-white transition-all duration-200";

Instagram은 다른 SNS와 달리 URL 공유 API가 없다. 그래서 프로필 링크로 연결하는 방식으로 처리했다.

Dark Place에도 동일하게 DarkShareButtons에 Instagram을 추가했는데, 여기는 --dp-* CSS 변수를 사용하는 inline style 방식이라 별도로 작업했다.

Buttondown 뉴스레터 연동

이메일 구독 기능은 예전부터 추가하고 싶었다. Buttondown을 선택한 이유는 API가 단순하고, 무료 플랜이 충분했기 때문이다.

연동 구조는 이렇다.

  1. NewsletterSignup.tsx 컴포넌트에서 이메일 입력
  2. /api/subscribe Route Handler로 POST 요청
  3. Route Handler가 Buttondown API를 호출
// src/app/api/subscribe/route.ts
const res = await fetch("https://api.buttondown.com/v1/subscribers", {
  method: "POST",
  headers: {
    Authorization: `Token ${apiKey}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ email, tags: ["website"] }),
});

이미 구독한 사용자가 다시 신청하면 409가 오는데, 이걸 "이미 구독 중"으로 처리하면 기존 구독자도 불편 없이 사용할 수 있다.

Dark Place에도 DarkNewsletterSignup을 별도로 만들었다. "새 글 알림 받기" 대신 "보안 뉴스 알림 받기"로 문구를 바꿔서 맥락에 맞게 했다.

디자인 리뷰 — 두 번의 리디자인

처음 구현했을 때 AuthorBio는 왼쪽에 세로 선이 있는 스타일이었고, NewsletterSignup은 위에 그라디언트 라인이 있었다. 배포하고 보니 너무 밋밋했다.

첫 번째 시도는 "고급스럽게" 만들어보겠다고 그라디언트 보더, Author 뱃지 등을 넣었는데 — 솔직히 별로였다. 선만 바뀌었을 뿐 느낌이 크게 달라지지 않았다.

두 번째 시도에서 방향을 완전히 바꿨다.

  • 왼쪽/위 선을 전부 제거
  • bg-[var(--bg-secondary)] 배경의 라운드 카드로 통일
  • 세 컴포넌트(ShareButtons, AuthorBio, Newsletter)를 space-y-5로 묶어서 시각적 흐름 확보

이렇게 하니까 글 본문과 하단 영역이 자연스럽게 분리되면서도, 각 요소가 같은 패밀리에 속한 느낌이 났다.

코드 리뷰에서 잡은 버그

구현 후 코드 리뷰를 돌렸는데, 재미있는 버그를 하나 잡았다.

/* 이렇게 쓰면 opacity가 적용되지 않는다 */
bg-[var(--bg-secondary)]/60

/* Tailwind의 /60 modifier는 bg-red-500/60 같은
   내장 컬러에만 동작한다. arbitrary value에는 무시됨 */

Tailwind의 opacity modifier(/60)는 bg-red-500 같은 내장 유틸리티에서만 동작한다. bg-[var(--css-variable)]에 붙이면 조용히 무시된다. 빌드 에러가 안 나니까 놓치기 쉬운 패턴이다.

내비게이션 개선

White Place와 Dark Place가 별개의 사이트처럼 운영되고 있었다. 서로 오가는 링크가 없어서 Dark Place의 존재를 모르는 사용자가 많았을 것이다.

White Place 네비바에 "Dark Place" 링크를, Dark Place에 "White Place" 복귀 링크를 추가했다. AI 뉴스 페이지도 네비바에 노출시켰다.

Playground 리더보드

12개 게임의 점수가 각각 localStorage에 흩어져 있어서, 전체를 한눈에 볼 수 있는 통합 리더보드를 만들었다. 게임별 필터, 상위 3명 메달 아이콘 등을 포함했다.

TypeScript에서 [...new Set()] 스프레드가 downlevelIteration 없이는 동작하지 않는 문제가 있어서 Array.from(new Set())으로 우회했다.

Sitemap 날짜 수정

기존 sitemap은 모든 URL의 lastModifiednew Date()였다. 매 빌드마다 오늘 날짜로 찍히니까, 검색 엔진 크롤러 입장에서는 전체 사이트가 매번 변경된 것처럼 보였다.

각 URL에 실제 포스트의 최신 날짜를 반영하도록 수정했다.

구독 버튼 디자인

마지막으로 구독 버튼을 빨간색(bg-red-500)으로 바꾸고, hover 시 살짝 커지는 모션을 추가했다.

className="bg-red-500 text-white hover:bg-red-600
  hover:scale-105 active:scale-95
  transition-all duration-200"

hover:scale-105로 살짝 커지고, active:scale-95로 누르면 움츠러드는 피드백을 넣었다. 미세한 차이지만 클릭하고 싶은 느낌이 확실히 다르다.

배운 점

하루 만에 이 정도 작업을 할 수 있었던 건, 계획을 먼저 세우고 영향도 순으로 정렬한 덕분이다. Tier 1 Quick Wins부터 처리하니까 빠르게 성과가 보이고, 그 모멘텀으로 Tier 2~3까지 밀어붙일 수 있었다.

디자인은 처음부터 완벽하게 만들려고 하지 말고, 배포 후 실제로 보면서 반복하는 게 낫다. 코드에서는 괜찮아 보여도 실제 사이트에서 보면 느낌이 다른 경우가 많았다.

그리고 코드 리뷰는 반드시 하자. Tailwind opacity 버그처럼 빌드에서 안 잡히는 문제는 눈으로 봐도 놓치기 쉽다.

Share
JJY
JJYAuthor

AI, 웹 보안, 개발 환경에 관심이 많습니다.

새 글 알림 받기

스팸 없이 새 포스트만 전달합니다.

관련 포스트