GROWTH LOG STORIES

함께 만든 성장을,
기록으로 연결합니다.

AI와 함께 배우고, 직접 만들고, 서로 나눈
그로스로그 멤버들의 생생한 성장 기록을 만나보세요.

성장 기록
5기+이어진 커뮤니티
HEALTHIO
PROJECT

사진 한 장으로 시작하는 건강 루틴, Health IO

대표 이야기 읽기

ALL STORIES

성장의 순간을
한곳에서 만나보세요.

프로젝트의 시작부터 모임에서 나눈 인사이트까지,
그로스로그가 함께 만든 기록을 모았습니다.
개의 성장 기록

성장일지

[GL 3기 9회차 ②] 올리면 알아서 되는 것들 — PR 미리보기 배포, CSS가 SEO에 닿는 지점 🌱

GROWTH LOG🌱 2026. 8. 7. 06:00

안녕하세요! 그로스로그 입니다!😊 🌱

PR을 올리면 자동으로 배포되고 접속 주소가 댓글로 달리는 구조, 그리고 CSS 문법이 궁금해서 시작한 질문이 검색 순위까지 닿은 기록. 3기 9회차 두 번째입니다.

성장일지는 멤버들이 2주에 한 번씩 자신의 성장을 기록하는 활동이에요. 매 회차 운영진이 모든 글을 읽고, 그중 특히 마음에 남은 글을 함께 골라 큐레이션합니다.

앞선 큐레이션에서 모듈 시스템과 Flutter 도전기를 다뤘는데요, 이번 두 편은 "이걸 왜 신경 써야 하는가"에 끝까지 답한 글들입니다.

 

01

🔄 GitHub Action으로 EC2에 블루그린 배포 및 PR 코멘트

김종진 님

이런 내용이에요
PR을 올리면 자동으로 배포되고, 접속할 수 있는 주소가 PR 댓글로 달리는 파이프라인을 만든 기록입니다.
블루-그린 방식으로 두 개의 슬롯을 번갈아 쓰고, nginx 리버스 프록시로 전환하며, 헬스체크까지 거칩니다.
그리고 특별한 점은 "왜 EC2를 골랐는가"를 먼저 설명한다는 거예요. Vercel이나 S3+CloudFront 같은 쉬운 길을 두고요.

7회차에서 홍범영 님이 CI/CD 뼈대를 세웠다면, 이 글은 한 단계 더 나아간 형태입니다.

조금 더 들어가 보면

먼저 문제 정의가 명확합니다. 팀에서 필요했던 건 이거였어요.

· PR을 올리면 배포돼서 볼 수 있어야 함

· 번거롭게 찾아가지 않고 PR 코멘트 등으로 바로 접근 가능했으면 함

여기에 저자가 스스로 추가한 것들이 있습니다.

· 자동 배포 외에 원하는 커밋으로 배포하는 수동 배포

· 문제가 생겼을 때 비교할 수 있게 기존 형상은 남겨두기

· PR 생성 때만이 아니라 커밋이 추가될 때마다 최신 상태로 동기화

"기존 형상은 남겨두기"가 블루-그린 방식으로 이어집니다. 슬롯을 두 개 두고 번갈아 쓰면, 새 버전에 문제가 있을 때 이전 것과 바로 비교할 수 있거든요.

🧭 그런데 왜 EC2였나

이 글에서 가장 눈여겨볼 부분입니다. 정적 웹페이지라면 S3+CloudFront나 Vercel로 훨씬 쉽게 배포할 수 있는데, 굳이 EC2를 골랐어요. 이유를 셋으로 듭니다.

리소스 낭비 감소"프론트만이라면 서버 없이 배포할 수 있지만 백단을 생각하면 호스팅할 서버가 필요하다." 어차피 서버가 필요한데, 개발 단계에서는 그 서버에 여유가 남으니 굳이 밖으로 뺄 이유가 없다는 판단입니다.

완전한 통제감"프론트쪽들은 잘 모르기 때문에.. 동작하는 것도 보고 부하는 얼마나 걸리는지 보고" 배우려는 목적이 섞여 있어요. 관리형 서비스는 편하지만 안에서 무슨 일이 일어나는지 안 보입니다.

콜드 스타트와 CDN 캐시 — 서버리스는 트래픽이 없으면 서버를 내려서 첫 접속이 느리고, CDN은 배포 후에도 캐시가 남아 바로 반영이 안 됩니다. 스테이징은 "빠르게 개발 내용을 확인"하는 게 목적이니 둘 다 맞지 않았다는 거죠.

그리고 이 문장이 좋았습니다.

"(근데 그냥 안 쓸 이유 찾아서 억까한 거 같기도)"

자기 판단을 스스로 의심하는 여유요. 그리고 마지막에 "어느 정도 윤곽 나오고 실제 배포단에서는 효율적 사용을 위해 S3+CDN으로 넘어갈 부분은 넘길 듯"이라고 열어둡니다. 지금은 이게 맞고, 나중엔 바뀔 수 있다는 태도예요.

📝 원문에서는 이렇게 나눕니다

워크플로가 두 가지 방식으로 실행되도록 만들어져 있습니다.

on:
  workflow_dispatch:
    inputs:
      commit:
        description: "8자리 short SHA (비워두면 브랜치의 최신 커밋)"
        required: false
  pull_request:
    types: [opened, synchronize, reopened]

pull_request의 세 이벤트가 각각 다른 상황을 잡습니다. opened(PR을 새로 열 때), synchronize(커밋을 추가로 밀 때), reopened(닫았다 다시 열 때). 이 셋을 다 넣어야 "항상 최신 상태가 배포되어 있다"가 성립합니다.

그리고 두 경로에서 온 요청이 같은 코드로 처리되도록 SHA를 계산합니다.

if [ "${{ github.event_name }}" = "pull_request" ]; then
  FULL_SHA="${{ github.event.pull_request.head.sha }}"
else
  if [ -n "${{ github.event.inputs.commit }}" ]; then
    FULL_SHA=$(git rev-parse ${{ github.event.inputs.commit }})
  else
    FULL_SHA=$(git rev-parse HEAD)
  fi
fi

입구는 둘인데 그 뒤는 하나입니다. 자동이든 수동이든 결국 "어떤 커밋을 배포할 것인가" 하나로 모으는 거죠. 분기를 앞쪽에서 끝내는 좋은 구조입니다.

run-name을 동적으로 만든 것도 실용적입니다. 실행 목록에서 "Deploy PR #12 '로그인 개선' / a1b2c3d"처럼 보이니, 어느 게 뭔지 바로 알 수 있어요.

🧭 그리고 정직한 메모

문서 안에 이런 줄이 있습니다.

"전달 이후 헬스 체크 < 배포된 서비스에 대한 헬스체크가 아닐 수 있어 업데이트 필요"

아직 불완전하다는 걸 문서에 적어둔 겁니다. 포트가 열렸는지만 보고 있어서, 서버는 떴는데 애플리케이션이 제대로 안 돌아가는 경우를 못 잡을 수 있다는 뜻이에요.

이런 메모가 값진 이유가 있습니다. 완성된 것처럼 적어두면 나중에 아무도 못 고칩니다. 아는 사람이 "여기 아직 덜 됐다"고 남겨둬야 다음에 손댈 수 있어요.

큐레이터 노트

"왜 이 방식인가"를 먼저 쓴 글이라 뽑았습니다.

기술 글에서 가장 자주 빠지는 게 선택의 근거입니다. "GitHub Actions로 EC2에 배포했다"는 결과만 있으면, 읽는 사람은 그게 자기 상황에도 맞는지 알 수 없어요. 이 글은 더 쉬운 대안들을 알면서도 왜 안 골랐는지를 먼저 적었습니다.

개발 편의를 위한 자동화라는 점도 좋았어요. 배포 파이프라인은 보통 운영을 위해 만드는데, 이건 "팀원이 PR을 올리면 바로 확인할 수 있게" 만든 겁니다. 리뷰어가 코드만 보고 상상하는 것과, 실제 화면을 눌러보고 리뷰하는 건 완전히 다르니까요.

저자의 블로그를 보면 KANS, AEWS, PKOS 같은 쿠버네티스 스터디 기록이 잔뜩 있고, CKS·ICA 자격증 후기도 있습니다. 인프라를 깊게 파고 있는 분이에요. 그런 분이 프론트엔드 배포를 직접 만져보며 "프론트쪽은 잘 모르기 때문에"라고 쓴 게 인상적이었습니다.

원문 읽으러 가기  ↗www.logonme.click/activities/etc/gha-deploy-pr-comment/
 

02

🎯 CSS 문법 뒤엔 자료구조가 있다

강단 님

강단 님 원문에 실린 이미지
이런 내용이에요
질문 하나에서 시작합니다. "CSS를 작성하다 보면 마치 '키-값' 쌍으로 데이터를 어딘가에 넘겨주는 듯한 느낌이 든다. 정말 CSS는 그런 식으로 동작하는 걸까?"
답을 찾아 브라우저의 CSS 파서까지 들어갑니다. Chrome(Blink)의 CSSParser.cpp 함수 시그니처까지요.
그리고 거기서 멈추지 않고 "그래서 이게 비즈니스에 무슨 이득인가"까지 갑니다. 답은 Core Web Vitals와 SEO였어요.

3회차에서 HTML 스트림을, 5회차에서 SVG 마스크를 다루셨던 그분입니다. 이번 글은 질문 하나가 어디까지 갈 수 있는지를 보여줍니다.

조금 더 들어가 보면

출발은 문법의 생김새였습니다. CSS는 속성: 값 형태인데, JSON이나 자바스크립트 객체와 닮았거든요.

.box {
  color: red;
  font-size: 16px;
}
{
  "color": "red",
  "font-size": "16px"
}

그리고 실제로 그렇다는 걸 Chrome 소스에서 확인합니다.

bool CSSParser::parseDeclarationList(
    const CSSParserContext& context,
    MutableStylePropertySet* propertySet,
    const String& declaration)

propertySet이라는 key-value 구조체에 저장된다는 게 함수 시그니처에 그대로 드러납니다. "느낌이 그렇다"를 소스 코드로 확인한 거예요.

그래서 이런 결론이 나옵니다.

"CSS는 스타일을 정의하는 언어이지만, 브라우저는 그것을 구조화된 데이터로 받아들인다."

🧭 그러면 "좋은 CSS"는 좋은 데이터 구조

이 관점이 잡히면 그다음이 자연스럽게 이어집니다. 글은 데이터 관리 관점에서 나쁜 CSS를 셋으로 정리해요.

1. 클래스 없이 태그만 사용div { color: red; } "데이터에서 key 없이 value만 나열한 것처럼 스코프가 없음."

2. 중복이 많고 규칙이 없음 — 같은 값을 여러 클래스가 각자 들고 있으면 DRY 위반입니다. 하나 바꾸려면 여러 곳을 고쳐야 해요.

3. 의미 없는 네이밍.red-font, .big-text 같은 것들이요. 스타일이 바뀌면 클래스명도 바꿔야 하는 이상한 의존이 생깁니다. 빨간색을 파란색으로 바꾸면 .red-font가 파란 글씨가 되죠.

반대로 좋은 CSS는 이렇습니다.

.card__title {
  font-size: 16px;
  color: var(--primary-text);
}

역할 기반 네이밍(title) + 범위(card__) + 변수로 추상화(var(--primary-text)). 글의 표현대로 "데이터베이스의 정규화처럼 데이터 재사용과 일관성 확보"입니다.

⚡ 그리고 "정답이 없는 영역"을 인정한 것

여기서 글이 아주 정직해집니다.

"유지보수 쉬움, 리팩토링 쉬움, 버그 줄어듦 관점은 주관적이다. (…) 어떤 팀은 BEM이 최고라 하고, 어떤 팀은 Tailwind가 최고라 할 수 있는 '정답'이 없는 영역이다."

네이밍 규칙 논쟁에는 답이 없다는 걸 인정합니다. 그러고 나서 답이 있는 영역으로 넘어가요.

"그럼에도 CSS의 데이터 관리를 한번쯤 고려하며 작업해봐야 할 의의는 렌더링 최적화에 있다."
/* 성능에 부담을 주는 구조 */
div ul li a span:hover {
  color: red;
}

이런 셀렉터가 왜 느릴까요. 브라우저는 셀렉터를 오른쪽에서 왼쪽으로 읽습니다. span을 다 찾고, 각각의 부모가 a인지 보고, 그 부모가 li인지 보고… 조건이 안 맞으면 되돌아가 다시 확인합니다. DOM이 클수록 이 비용이 커져요.

.btn--active {
  color: red;
}

클래스 하나면 바로 매칭됩니다. 그래서 글이 짚은 대로 "이건 사람에 관계없는 기술적 진실"이에요.

🎯 그리고 비즈니스까지

이 글이 특별해지는 지점이 마지막 장입니다. 스스로 이렇게 묻습니다.

"브라우저 최적화를 고려해서 작업한다는 게 비즈니스에 주는 이점이 뭔가?"

답은 Core Web Vitals입니다. Google이 검색 순위에 반영하는 세 지표요.

· LCP — 가장 큰 콘텐츠가 뜨기까지의 시간 (2.5초 이내 'Good')

· FID — 사용자 입력에 반응하기 시작할 때까지의 시간

· CLS — 예기치 않게 요소가 밀린 총 거리 (0.1 이하 'Good')

그리고 CSS가 이 점수를 어떻게 깎는지 구체적인 예를 듭니다.

.hero {
  font-size: 0;
  visibility: hidden;
  animation: fadeIn 2s ease-in;
}

페이드인 애니메이션이 LCP를 늦춥니다. 브라우저는 실제로 보이게 된 시점을 재거든요. 2초짜리 애니메이션을 걸면 그만큼 점수가 나빠집니다. 예쁘게 하려던 게 검색 순위를 깎는 거죠.

<img src="/banner.png">

widthheight가 없으면 CLS가 올라갑니다. 이미지가 로드되기 전엔 브라우저가 크기를 몰라서 자리를 못 잡아요. 그러다 이미지가 뜨면 아래 내용이 확 밀립니다. 글 읽다가 갑자기 화면이 움직이는 그 짜증나는 경험이요.

방지법도 정확합니다. 이미지에 크기나 aspect-ratio 지정, font-display: swap으로 글꼴 깜빡임 방지, 동적 요소는 자리 미리 잡아두기.

🧭 그리고 반성

마지막 장의 제목이 "반성"입니다.

"문법 모양으로 시작한 의문점이 애니메이션, 인터랙션을 이유로 나도 모르게 LCP를 깎아 먹고 있었다는 것을 깨닫게 했다."

자기가 지금까지 해온 걸 다시 보게 된 겁니다. 예쁘게 만들려고 넣은 페이드인이 실은 점수를 깎고 있었다는 것을요.

그리고 균형도 잊지 않습니다. "시맨틱, 콘텐츠 내용, 유저 진입 및 정체 시간 등이 월등하면 CSS가 주는 차이는 미미해질 수도 있겠지만." CSS 최적화가 만능이 아니라는 것도 아는 상태예요.

큐레이터 노트

질문 하나가 끝까지 간 글이라 뽑았습니다.

시작은 "CSS가 키-값 같은데 진짜 그런가?"라는 사소한 궁금증이었습니다. 그런데 그게 Chrome 소스 코드 → 좋은 CSS 구조 → 셀렉터 성능 → Core Web Vitals → SEO → 검색 순위까지 이어졌어요.

"그래서 비즈니스에 무슨 이득인가"까지 물은 게 이 글을 특별하게 만듭니다. 기술 글은 대개 기술에서 끝나거든요. 그런데 실무에서는 "그래서 이걸 왜 해야 하죠"에 답할 수 있어야 시간을 쓸 수 있습니다.

"정답이 없는 영역"과 "기술적 진실"을 구분한 것도 좋았습니다. BEM이냐 Tailwind냐는 팀마다 다르지만, 셀렉터 성능은 측정 가능한 사실이에요. 어디까지가 취향이고 어디부터가 사실인지를 가른 게 이 글의 논리를 단단하게 만듭니다.

그리고 마지막 줄에 웃었습니다.

"🫠 오늘 최고 온도 37도 🫠 블로그 쓰는 시간 < 썸네일 만드는 시간."

7월 말 폭염 속에서 이만한 글을 쓰셨네요. 3기에 네 번 큐레이션되신 분이고, 매번 "궁금해서 파봤다"로 시작합니다.

원문 읽으러 가기  ↗kangdanne.tistory.com/343

💡 두 편을 겹쳐 읽으며

한 편은 배포 파이프라인, 한 편은 CSS 구조입니다. 완전히 다른 영역이죠.

그런데 두 글이 똑같이 한 일이 있습니다. 둘 다 "그래서 이게 왜 필요한가"에 끝까지 답했어요.

김종진 님은 "왜 EC2인가"에 답합니다. Vercel이 더 쉽다는 걸 알면서도, 콜드 스타트와 CDN 캐시 때문에 스테이징 목적에 안 맞는다고 짚었어요.

강단 님은 "왜 CSS 구조를 신경 써야 하는가"에 답합니다. "유지보수가 쉬워진다"는 주관적이니, Core Web Vitals와 검색 순위라는 측정 가능한 근거까지 갔고요.

둘 다 "좋으니까 하세요"에서 멈추지 않았습니다.

이게 왜 중요한가 하면, 개발자가 하는 일의 상당수는 눈에 안 보이기 때문입니다. 배포 자동화도, CSS 정리도 화면에 새 기능이 늘지 않아요. 그래서 "그거 왜 하세요?"라는 질문을 받습니다.

그때 "코드가 깨끗해져서요"라고 답하면 설득이 안 됩니다. "리뷰어가 코드만 보고 상상하는 대신 실제 화면을 눌러볼 수 있어요", "LCP가 개선되면 검색 순위가 올라갑니다"라고 답할 수 있어야 시간을 얻어요.

기술을 기술로 설명하는 것과, 그 기술이 만드는 결과로 설명하는 것 — 두 글이 후자를 해냈습니다.

이번 회차에서 가져갈 것

1. PR마다 배포된 주소를 붙여주기. 리뷰어가 코드만 읽는 것과 실제로 눌러보는 것은 다릅니다. pull_requestopened·synchronize·reopened를 다 걸어야 항상 최신이 유지돼요.

2. 자동/수동 두 입구를 만들되 처리는 하나로. 분기는 앞에서 끝내고 "어떤 커밋을 배포할 것인가" 하나로 모으면 코드가 단순해집니다.

3. 덜 된 부분은 문서에 적어두기. "이 헬스체크는 아직 부정확하다" 같은 메모요. 완성된 것처럼 두면 아무도 못 고칩니다.

4. 셀렉터는 짧고 클래스 기반으로. div ul li a span:hover는 브라우저가 오른쪽에서 왼쪽으로 되짚어 찾습니다. 클래스 하나면 바로 매칭돼요.

5. 이미지에는 크기를 꼭 지정하기. width/height 또는 aspect-ratio가 없으면 로드될 때 아래 내용이 밀립니다(CLS). 그리고 첫 화면에 페이드인 애니메이션은 LCP를 깎아요.


좋은 글 남겨주신 김종진 님, 강단 님께 감사드립니다! 😊

다음 큐레이션으로 다시 찾아오겠습니다! 🌱

- 에디터 · 성장일지 큐레이터 -


※ 본 큐레이션은 각 저자가 공개한 글을 소개하는 것이며, 모든 원문의 저작권은 저자에게 있습니다. 저자 본인의 요청이 있을 경우 즉시 수정 또는 삭제합니다.

WRITE YOUR GROWTH

오늘의 배움을
내일의 성장으로.

기록은 나를 돌아보게 하고, 공유는 우리를 연결합니다.
당신의 다음 성장 이야기도 그로스로그에서 시작해보세요.

커뮤니티 둘러보기