안녕하세요! 그로스로그 입니다!😊 🌱
UI 라이브러리를 내 프로젝트 안으로 가져와 직접 고치는 방식, 그리고 "코드는 없지만 코드를 담을 자리를 먼저" 만든 기록. 3기 7회차 첫 번째입니다.
성장일지는 멤버들이 2주에 한 번씩 자신의 성장을 기록하는 활동이에요. 매 회차 운영진이 모든 글을 읽고, 그중 특히 마음에 남은 글을 함께 골라 큐레이션합니다.
이번 회차는 큐레이션이 네 편이라 두 번에 나눠 소개합니다. 먼저 본격적으로 만들기 전에 기반을 놓는 두 편이에요.
01
🎨 Next.js 프로젝트에 Shadcn/ui 사용하기
정민아 님
Next.js 15 + TailwindCSS + Shadcn/ui를 처음 붙이는 것부터 커스터마이징까지 한 편에 정리한 글입니다.
프로젝트 생성 → CLI 초기화 → Tailwind 설정 확인 → 컴포넌트 추가 → 공통 스타일 수정 → 페이지별 오버라이드 → 재사용 컴포넌트 추출까지 7단계로 이어져요.
단순 설치 안내가 아니라, 팀에서 실제로 UI를 관리하는 방식까지 갑니다.
목차가 곧 이 글의 값어치입니다. 설치에서 끝나지 않고 "그다음 어떻게 관리할 것인가"로 이어져요.
조금 더 들어가 보면
Shadcn/ui는 여느 UI 라이브러리와 성격이 다릅니다. 글의 마지막 문장이 그 차이를 짚어요.
"프로젝트 내에서 직접 편집할 수 있다"가 핵심입니다.
보통 UI 라이브러리는 npm install로 설치합니다. 그러면 코드가 node_modules 안에 들어가요. 내가 못 건드리는 자리입니다. 디자인을 바꾸려면 라이브러리가 열어둔 옵션 안에서만 가능하고, 원하는 게 없으면 !important로 억지로 덮어쓰거나 포기하게 되죠.
Shadcn/ui는 컴포넌트 코드를 내 프로젝트로 복사해 넣습니다.
npx @shadcn/ui@latest add button
이 명령이 하는 일은 설치가 아니라 components/ui/button.tsx 파일을 만들어주는 것입니다. 그 순간부터 그건 내 코드예요. 마음대로 고칠 수 있습니다.
의존성이 아니라 출발점을 주는 방식이라고 보면 정확합니다.
🧭 세 층으로 나눈 커스터마이징
이 글에서 가장 실용적인 부분입니다. 스타일을 바꾸는 방법을 세 단계로 갈라놨어요.
1층 — 공통 스타일 (전역)
components/ui/button.tsx를 직접 고칩니다.
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors",
{
variants: {
variant: {
default: "bg-purple-600 text-white hover:bg-purple-700", // 변경!
secondary: "bg-gray-100 text-gray-900",
},
size: {
sm: "h-8 px-3",
md: "h-10 px-4",
lg: "h-12 px-6",
},
},
defaultVariants: { variant: "default", size: "md" },
}
)
모든 페이지의 버튼이 한 번에 바뀝니다. 브랜드 색이 바뀌었을 때 여기 한 줄만 고치면 되죠.
cva(class variance authority)가 하는 일도 눈여겨볼 만합니다. "어떤 조합이 가능한가"를 코드로 정의하는 거예요. variant와 size가 정해져 있으니, 팀원이 아무 클래스나 붙이는 걸 막고 정해진 조합 안에서 쓰게 만듭니다. 디자인 시스템의 시작입니다.
2층 — 페이지별 오버라이드
<Button className="bg-green-600 hover:bg-green-700 text-white"> 홈 전용 버튼 </Button>
특정 화면에서만 다르게 하고 싶을 때 className을 덧붙입니다. 글이 짚은 주의사항이 정확해요.
<Button className="text-sm text-base">와 같이 폰트 크기에 대한 두 가지 스타일을 동시에 쓰면 당연히 안 된다."Tailwind는 나중에 쓴 게 이기는 게 아니라 CSS 우선순위를 따릅니다. 그래서 같은 속성을 두 번 지정하면 예측이 어려워져요. tailwind-merge 같은 도구를 함께 쓰는 이유가 이겁니다.
3층 — 재사용 컴포넌트 추출
// components/common/PrimaryButton.tsx
import { Button, ButtonProps } from "@/components/ui/button"
export function PrimaryButton(props: ButtonProps) {
return (
<Button
{...props}
className={`bg-primary hover:bg-primary/90 text-white ${props.className}`}
/>
)
}
같은 오버라이드를 여러 곳에서 반복하고 있다면 아예 이름을 붙여 컴포넌트로 빼는 단계입니다.
글의 이 문장이 좋았어요.
디자인 시스템이 없어도 패턴은 있다는 관찰입니다. 그 패턴에 이름을 붙여두면 그게 시작이 되는 거죠.
전역 → 페이지별 → 추출이라는 순서 자체가 실무의 흐름입니다. 처음엔 기본값을 맞추고, 예외가 생기면 그 자리에서 덮고, 예외가 반복되면 새 이름을 주는 것.
큐레이터 노트
설치 안내에서 멈추지 않은 글이라 뽑았습니다.
라이브러리 도입 글은 대개 "설치하고 이렇게 쓰세요"에서 끝납니다. 그런데 실무에서 진짜 어려운 건 쓰기 시작한 다음이에요. 화면이 스무 개로 늘었을 때 스타일이 제각각 되지 않게 하는 일이요.
이 글은 그 부분에 답을 줍니다. 어디를 고치면 전체가 바뀌고, 어디를 고치면 그 화면만 바뀌는지를 나눠뒀어요. 혼자 만들 때는 몰라도 되지만, 여럿이 만들 때는 반드시 정해야 하는 규칙입니다.
참고 자료를 네 개 밝힌 것도 좋았습니다. Shadcn/ui 공식 문서, Radix UI, Tailwind, cva. Shadcn/ui가 이 셋을 조합한 물건이라는 걸 알고 정리했다는 뜻이에요.
프로필의 "함께 나아가는 개발자💪"라는 문구가 이 글의 성격과도 잘 맞았습니다. 혼자 쓰고 마는 정리가 아니라 팀에서 같이 쓸 수 있는 형태로 만들어졌거든요.
02
🏗️ Leaf 성장일지 #2 — 인프라는 처음이라, CI/CD 구축기
홍범영 님
사이드 프로젝트 Leaf의 개발 서버 인프라를 처음부터 구축한 기록입니다.
EC2 생성 → Ubuntu 세팅 → Docker 설치 → Docker Compose 구성 → 서비스별 Dockerfile → GitHub Actions로 CI/CD 파이프라인까지 8단계로 이어져요.
특별한 점은 단계마다 "시행착오"를 따로 적어뒀다는 겁니다. 그리고 블로그 이름이 "기획자의 개발 알아가기"예요.
첫 문단이 이 글의 성격을 정확히 말해줍니다.
조금 더 들어가 보면
글이 먼저 정리한 서버 4단계가 좋습니다.
1. 로컬 — 개발자의 개인 환경
2. 개발 서버 — 공동 테스트용, 작업 공유
3. 스테이징 서버 — 배포 전 최종 검증
4. 라이브 서버 — 실사용자 대상 서비스
그리고 지금 필요한 게 무엇인지를 판단합니다.
네 단계를 다 만들지 않았습니다. 지금 필요한 하나만 골랐어요. 6회차의 "규모에 맞는 선택"과 이어지는 판단입니다.
🧭 시행착오를 단계마다 적어둔 것
이 글의 진짜 값어치는 여기 있습니다. 각 단계 끝에 "시행착오"가 붙어 있어요.
EC2를 껐다 켜면 공인 IP가 바뀝니다. 어제 접속하던 주소로 오늘 안 되는 거죠. Elastic IP는 그 주소를 고정해줍니다. 처음 EC2를 쓰는 사람이 반드시 만나는 벽이에요.
chmod 600 ~/.ssh/config로 수정""WSL 터미널에서 도커 권한 오류 →
sudo usermod -aG docker $USER 후 재로그인""24.04 AMI를 실수로 선택 → 원래 목표였던 22.04와 버전 차이 발생"
전부 삽질 목록입니다. 그런데 이게 이 글을 가장 쓸모 있게 만들어요. 같은 자리에서 막힌 사람이 검색으로 찾아올 수 있는 내용이거든요.
특히 도커 권한 오류 → 재로그인은 3회차의 도커 글에서도 나왔던 부분입니다. 다들 여기서 한 번씩 막힌다는 뜻이죠.
⚙️ 그리고 CI/CD 파이프라인
마지막 단계에서 GitHub Actions로 자동 배포를 만듭니다.
name: CI/CD Pipeline
on:
push:
branches: [ main ]
workflow_dispatch:
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Login to Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ secrets.REG_USER }}
password: ${{ secrets.REG_PASS }}
...
- name: Deploy to EC2
uses: appleboy/ssh-action@v0.1.7
with:
host: ${{ secrets.EC2_HOST }}
username: ubuntu
key: ${{ secrets.EC2_SSH_KEY }}
script: |
cd ~/leaf
docker compose pull
docker compose up -d --build
흐름이 명확합니다. main에 푸시 → 이미지 빌드 → 레지스트리에 올림 → EC2에 접속해 새 이미지로 교체.
workflow_dispatch를 함께 넣은 것도 실용적입니다. 버튼으로 수동 실행할 수 있게 해두는 거예요. 배포가 실패했을 때 코드를 다시 푸시하지 않고 재시도할 수 있습니다.
그리고 Secrets 관리를 명시적으로 짚었습니다.
SSH 키를 코드에 넣으면 저장소에 접근할 수 있는 누구나 서버에 들어올 수 있습니다. 공개 저장소면 더 심각하고요. 처음 CI/CD를 만들 때 이걸 챙긴 게 중요합니다.
큐레이터 노트
기획자가 인프라를 만든 기록이라 뽑았습니다.
블로그 이름이 "기획자의 개발 알아가기"예요. 그리고 글 안에서도 반복해서 "제로 베이스"라고 밝힙니다. 그 상태에서 EC2부터 GitHub Actions까지 끝까지 갔어요.
이게 왜 값진가 하면, 기획자가 인프라를 이해하는 것과 못 하는 것의 차이가 팀에서 크기 때문입니다. "배포에 왜 시간이 걸리는지", "서버 비용이 왜 나가는지"를 아는 기획자와 모르는 기획자는 일정 잡는 방식이 달라요.
시행착오를 숨기지 않은 것도 좋았습니다. 24.04 AMI를 실수로 골랐다는 것까지 적어뒀어요. 잘한 것만 적으면 읽는 사람은 "나만 이렇게 헤매나" 싶어지는데, 이런 기록은 반대로 안심시킵니다.
그리고 마지막 문장이 마음에 남았어요.
"아직 인프라에 대해서 산적해있는 문제들이 많습니다. 네트워크 설정이랑 도메인도 연결해야 하고.."
스스로를 칭찬하고, 남은 일도 인정하는 균형이 좋았습니다. 실제로 다음 편(#3)에서 도메인 연결을 하셨더라고요. 연재로 이어가는 힘입니다.
💡 두 편을 겹쳐 읽으며
한 편은 UI 컴포넌트, 한 편은 서버 인프라입니다. 다루는 층이 정반대예요.
그런데 두 글이 하는 일은 같습니다. 둘 다 "본격적으로 만들기 전에 자리를 놓는 일"입니다.
홍범영 님의 표현을 그대로 빌리면 "코드는 없지만, 코드를 담을 '자리'를 먼저 만들어두는 작업"이죠. 정민아 님의 글도 마찬가지입니다. 화면을 만들기 전에 버튼이 어떻게 생길지, 어디를 고치면 어디까지 바뀌는지를 정해두는 일이니까요.
이런 작업에는 공통점이 있습니다. 당장은 눈에 보이는 결과가 없어요. 화면 하나 안 나오고 기능 하나 안 늘어납니다. 그래서 미루기 쉽고, 미루면 나중에 훨씬 비싸집니다.
버튼 스타일을 안 정하고 화면 20개를 만들면, 나중에 브랜드 색이 바뀔 때 20군데를 찾아야 합니다. 배포 자동화를 안 만들고 개발하면, 매번 손으로 올리다가 실수가 납니다.
그리고 두 글 모두 "지금 필요한 만큼만" 했습니다. 서버 4단계를 다 만들지 않고 개발 서버 하나만 세웠고, 디자인 시스템을 거창하게 짜지 않고 버튼부터 정리했어요.
기반을 놓되 과하지 않게 — 균형을 잡은 두 편이었습니다.
이번 회차에서 가져갈 것
1. 라이브러리를 고칠 수 있는지 확인하기. node_modules 안에 있으면 못 고칩니다. Shadcn/ui처럼 코드를 내 프로젝트로 가져오는 방식은 자유도가 완전히 달라요.
2. 스타일 수정은 세 층으로 나누기. 전역(컴포넌트 파일) → 페이지별(className) → 추출(래퍼 컴포넌트). 같은 오버라이드가 세 번 반복되면 이름을 붙일 때입니다.
3. EC2는 Elastic IP부터. 껐다 켜면 IP가 바뀝니다. 접속 주소가 매번 달라지는 번거로움을 처음에 없애두세요.
4. Secrets는 처음부터 분리하기. SSH 키나 토큰이 저장소에 들어가면 되돌릴 수 없습니다. 커밋 기록에 영원히 남거든요. GitHub Secrets를 처음부터 쓰세요.
5. 지금 필요한 단계만 만들기. 로컬·개발·스테이징·라이브를 다 갖출 필요는 없습니다. "지금 무엇이 없어서 불편한가"로 정하면 과하지 않아요.
좋은 글 남겨주신 정민아 님, 홍범영 님께 감사드립니다! 😊
7회차 두 번째 큐레이션으로 이어집니다! 🌱
- 에디터 · 성장일지 큐레이터 -
※ 본 큐레이션은 각 저자가 공개한 글을 소개하는 것이며, 모든 원문의 저작권은 저자에게 있습니다. 저자 본인의 요청이 있을 경우 즉시 수정 또는 삭제합니다.