GROWTH LOG STORIES

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

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

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

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

대표 이야기 읽기

ALL STORIES

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

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

성장일지

[GL 4기 1회차 ①] 이름을 붙이는 일과 나누지 않는 판단 — 시맨틱 태그, 리액트 리스트 렌더링 🌱

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

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

화면에는 똑같이 보이는데 구조는 전혀 다른 두 코드. 그 차이를 각자 다른 방향에서 파고든 두 편입니다.

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

4기는 2025년 9월에 시작됐습니다. 첫 회차부터 HTML의 밑바닥리액트 실무 감각이 나란히 올라왔어요.

 

01

🏷️ 웹 개발에서 중요한 시맨틱 태그(Semantic Tag)

진현주 님

진현주 님 원문에 실린 이미지
이런 내용이에요
HTML5의 시맨틱 태그를 정리한 글입니다. <header>, <nav>, <main>, <article>, <section>, <aside>, <footer> 일곱 개를 짚고, 왜 써야 하는지를 SEO · 웹 접근성 · 코드 가독성 세 갈래로 나눠 설명해요.
후반부에는 잘 안 쓰이는 태그들을 따로 모았습니다. <time>, <figure>, <address>, <mark>, <details>/<summary>. 예제 코드까지 붙였습니다.
글을 쓰게 된 계기가 앞머리에 있어요. 유퀴즈에서 본 시각 장애인 개발자의 이야기입니다.

기본 개념을 다루는 글인데, 시작하는 지점이 남달랐습니다.

조금 더 들어가 보면

시맨틱 태그를 배우면 대부분 이런 의문이 남습니다. "<div>로 해도 화면은 똑같은데 왜?"

맞습니다. 브라우저 화면만 놓고 보면 차이가 없어요. <header><div class="header">든 픽셀 단위로 동일하게 그려집니다. 그런데도 구분해서 쓰는 이유는, 화면을 보지 않고 이 문서를 읽는 존재들이 있기 때문입니다.

글이 든 첫 번째 독자는 스크린 리더예요.

🦮 랜드마크 — 스크린 리더 사용자의 목차

스크린 리더는 문서를 위에서부터 소리로 읽어 내려갑니다. 그런데 실제 사용자는 그렇게 안 써요. 처음부터 끝까지 다 듣는 건 너무 느리기 때문입니다.

대신 랜드마크(landmark) 단위로 건너뜁니다. "메인 콘텐츠로", "탐색 영역으로" 같은 명령으로 구역을 넘나들어요. 시력이 있는 사람이 페이지를 훑어보며 원하는 곳으로 눈을 옮기는 것과 같은 동작입니다.

이 랜드마크를 만드는 게 바로 시맨틱 태그입니다.

· <nav>navigation 랜드마크

· <main>main 랜드마크

· <header>banner 랜드마크

· <footer>contentinfo 랜드마크

· <aside>complementary 랜드마크

그러니까 <div>만 써서 만든 페이지는 목차가 없는 책입니다. 내용은 다 있지만 원하는 곳으로 갈 수가 없어요. 글이 "단순히 '구역'이라고만 인식한다"고 적은 게 이 상황입니다.

여기서 한 가지 덧붙이면, 시맨틱 태그는 ARIA 속성의 축약형이기도 합니다. <nav><div role="navigation">과 같은 역할을 하는데, 후자는 속성을 직접 써야 하고 전자는 태그만으로 됩니다. 접근성 표준(WAI-ARIA)에는 이런 원칙이 있어요.

"ARIA를 안 쓰는 것이 잘못 쓰는 것보다 낫다. 그리고 적절한 HTML 요소가 있다면 그것을 쓰라."

role을 손으로 붙이는 것보다 맞는 태그를 고르는 게 먼저라는 뜻입니다.

🔍 SEO — 순위보다 구조 이해

SEO 부분은 조금 정확하게 짚고 갈 만합니다.

글은 "검색 노출에 긍정적인 영향을 미칠 수 있습니다"라고 조심스럽게 썼는데, 이 표현이 정확해요. 시맨틱 태그를 썼다고 순위가 올라가는 게 아니거든요. 검색 엔진이 공식적으로 밝힌 순위 요인 목록에 <article> 같은 건 없습니다.

그럼 뭐가 달라지느냐면, 크롤러가 페이지를 어떻게 요약하는가입니다.

검색 결과에는 제목 아래에 두어 줄 요약이 붙죠. 이 요약을 뽑을 때 크롤러는 본문이 어디인지 알아야 합니다. <main><article>이 있으면 바로 찾고, 없으면 추측해야 해요. 추측이 빗나가면 사이드바 문구나 메뉴 텍스트가 요약으로 뽑히는 일이 생깁니다.

즉 시맨틱 태그의 효과는 "더 위로 올려준다"가 아니라 "엉뚱하게 읽히지 않게 한다"에 가깝습니다. 방어적인 효과예요. 그래도 충분히 쓸 이유가 됩니다.

📝 원문에서는 이런 태그를 소개합니다

후반부에 소개한 태그들이 실용적입니다. 특히 이 조합이요.

<details open>
  <summary>summary 텍스트</summary>
  <mark>details 내부</mark>
</details>

글이 짚은 대로 "tag가 css도 같이 내포되어 있다"는 점이 이 태그들의 핵심입니다.

<details>자바스크립트 없이 접었다 펼쳐집니다. 보통 아코디언을 만들려면 상태를 하나 두고, 클릭 핸들러를 붙이고, 높이 애니메이션을 넣고, 키보드 접근성까지 챙겨야 해요. 그런데 이 태그는 그걸 브라우저가 이미 다 해둔 상태입니다.

키보드 포커스도 되고, 엔터로 열리고, 스크린 리더에는 "접힘/펼침" 상태까지 안내됩니다. 직접 만들었다면 놓쳤을 것들이 기본으로 들어 있어요.

<time>도 비슷한 성격입니다.

<time datetime="2025-09-27">2025년 9월 27일</time>

사람은 "2025년 9월 27일"을 읽고, 기계는 datetime 속성의 2025-09-27을 읽습니다. 하나의 정보를 두 독자에게 각각 맞는 형태로 주는 구조예요. 이걸 <span>으로 쓰면 기계 입장에서는 그냥 글자 덩어리입니다.

큐레이터 노트

"왜 쓰는가"에 사람을 등장시킨 글이라 뽑았습니다.

시맨틱 태그 정리 글은 아주 많습니다. 대부분 태그 목록과 설명으로 끝나요. 이 글이 다른 건 출발점입니다.

"최근 유퀴즈에서 미국 월가에서 일하는 시각 장애인분의 영상을 보고 웹 개발자로서 접근성 측면을 더 고려해야겠다는 것을 느꼈었습니다."

기술 문서를 읽고 정리한 게 아니라, 실제 사람을 보고 나서 쓴 글입니다. 그래서 "접근성이 중요하다"는 문장이 상투구로 읽히지 않아요.

모르던 것을 솔직히 적은 부분도 좋았습니다. "저도 이번에 찾아보면서 알게 된 것들이 있는데요"라며 <details><mark>를 소개해요. 아는 걸 정리한 글이 아니라 쓰면서 배운 걸 그대로 남긴 글이라는 뜻입니다. 성장일지에 어울리는 태도예요.

4기 첫 회차에 접근성 이야기가 올라온 것도 반가웠습니다. 실무에서 자주 뒤로 밀리는 주제라서요.

원문 읽으러 가기  ↗realzzu.tistory.com/213
 

02

⚛️ 요즘 내가 리액트로 리스트를 렌더링 한다면

오세명 님

이런 내용이에요
배열로 UI를 그릴 때 <Item /> 컴포넌트를 만들 것인가, map 콜백 안에서 바로 JSX를 쓸 것인가. 이 선택에 대한 자기 기준을 정리한 글입니다.
결론이 통념과 반대예요. 기본값은 컴포넌트를 만들지 않는 쪽이고, "반드시 그래야 할 이유가 생길 때만" 추출합니다.
왜 그렇게 굳어졌는지를 되짚는 과정에서 LLM속도라는 두 키워드가 나옵니다. 마지막에는 자기 비중을 숫자로 밝혀요. 품질 3.5 대 속도 6.5.

"작업 과정을 되돌아보는 것도 성장에 도움이 될 것 같아서"라는 첫 문장으로 시작합니다. 무의식적으로 하던 선택을 언어로 꺼내보는 글이에요.

조금 더 들어가 보면

이 글이 건드리는 건 프론트엔드에서 거의 반사적으로 하는 판단입니다. "이거 컴포넌트로 빼야지."

대부분 그렇게 배웁니다. 반복되면 빼고, 길어지면 빼고, 재사용될 것 같으면 빼고요. 그런데 글은 그 기본값을 뒤집습니다.

"나는 먼저 전자로 작업을 하고 반드시 그래야 할 이유가 생길 때만 후자를 생각한다."

여기서 전자는 map 콜백 안에서 JSX를 만드는 것, 후자가 <Item />을 만드는 것입니다.

🎈 Props가 부풀어 오르는 과정

왜 그런 기준이 생겼는지는 글이 보여주는 변화 과정에 있습니다.

처음 <Item />은 깔끔합니다.

type Props = {
  id: string;
  title: string;
  description: number;
};

그런데 시간이 흐르고 UI를 확장하면 이렇게 됩니다.

type Props = {
  id: string;
  title: string;
  subTitle?: string;
  description?: string;
  onClick?: () => void;
};

혹은 이렇게요.

type Props = {
  id: string;
  title: string;
  subTitle?: React.ReactNode;
  description?: React.ReactNode;
  button?: React.ReactNode;
};

물음표가 계속 늘어납니다. 이게 이 글의 핵심 관찰이에요.

물음표 하나가 붙을 때마다 그 컴포넌트가 그릴 수 있는 화면의 경우의 수가 두 배가 됩니다. 물음표 네 개면 열여섯 가지죠. 그런데 이 컴포넌트를 열어봐도 실제로 쓰이는 조합이 몇 개인지는 알 수 없습니다. 호출하는 곳을 전부 찾아봐야 알아요.

글이 "인지적으로 더 부하가 왔었다"고 표현한 게 이 상태입니다.

두 번째 형태(React.ReactNode)는 더 미묘합니다. 타입만 보면 유연해 보이는데, <Item />이 자기가 뭘 그리는지 모르는 상태가 돼요. 그저 받아서 자리에 꽂아줄 뿐입니다. 컴포넌트가 아니라 레이아웃 껍데기가 된 거죠.

📝 원문에서는 이렇게 씁니다

대안으로 제시한 두 가지 형태입니다. 둘 다 map 안에서 끝납니다.

{items.map((item) => {
  const showSubTitle = /* ... */
  const showDescription = /* ... */
  const showButton = /* ... */
  return (
    <article key={item.id}>
      <h2>{item.title}</h2>
      {showSubTitle && <h3>{item.subTitle}</h3>}
      {showDescription && <p>{item.description}</p>}
      {showButton && <button onClick={onClick(item)}>Show more</button>}
    </article>
  )
})}
{items.map((item) => {
  const description = /* ... */ ? <p>{item.description}</p> : null
  const subTitle = /* ... */ ? <h3>{item.subTitle}</h3> : null
  const button = /* ... */ ? <button onClick={onClick(item)}>Show more</button> : null
  return (
    <article key={item.id}>
      <h2>{item.title}</h2>
      {subTitle}
      {description}
      {button}
    </article>
  )
})}

앞의 것은 조건과 JSX가 붙어 있고, 뒤의 것은 조건 계산을 위로 몰아두고 아래에서는 배치만 합니다. 뒤쪽이 반환문 부분을 훨씬 조용하게 만들어요. 화면 구조가 한눈에 들어옵니다.

그리고 어느 쪽이든, 아까 물음표가 넷 붙었던 그 정보가 여기서는 전부 눈에 보입니다. 어떤 조건일 때 뭐가 나오는지 파일을 옮겨 다니지 않고 알 수 있어요.

🤖 LLM이 이유로 등장한다는 것

글이 든 이유 중 하나가 눈길을 끕니다.

"특히 LLM이 짠 코드를 검토할 때는 만들어진 맥락이 무엇인지 더 편히 읽을 수 있었다."

코드 구조를 정하는 기준으로 "AI가 짠 걸 내가 검토하기 좋은가"가 들어온 겁니다. 몇 년 전이라면 나오지 않았을 근거예요.

생각해보면 자연스럽습니다. 코드를 짜는 시간보다 읽고 판단하는 시간의 비중이 커졌으니까요. 그리고 검토는 한 화면 안에서 위아래로 할 때가 가장 빠릅니다. 파일 두 개를 오가면 흐름이 끊겨요.

여기서 앞의 진현주 님 글과 겹치는 지점이 생깁니다. 둘 다 "이 코드를 누가 읽는가"를 먼저 정하고 있어요.

🔁 재사용에 대한 태도

가장 도발적인 부분입니다.

"이게 재사용될 수 있다고 생각하는가? 내 경험상 이 생각은 오버엔지니어링으로 이어졌다."

그리고 정말 재사용이 필요할 때조차 "적극적으로 중복해서 코드를 작성한다"고 합니다. 이유는 "UI가 자체적인 맥락을 가지고 tweak되는 케이스를 많이 봐서."

DRY(반복하지 마라)를 배운 입장에서는 거슬릴 수 있는 말인데, 실무 감각으로는 짚이는 데가 있습니다.

두 화면에서 똑같이 생긴 카드를 하나로 합쳤다고 해보죠. 몇 주 뒤 한쪽에만 뱃지가 붙습니다. showBadge 하나 추가하죠. 또 몇 주 뒤 다른 쪽만 여백이 넓어집니다. compact 추가. 그러다 보면 어느 쪽에서도 온전히 자기 것이 아닌 컴포넌트가 남습니다.

처음부터 복사해뒀다면 각자 자기 길로 갔을 겁니다. "겉모습이 같다"와 "같은 것이다"는 다르다 — 이걸 구분하는 게 요점이에요.

다만 글이 단서를 달아둔 것도 놓치면 안 됩니다. "(감당 가능한 수준 하에)". 무조건 복사하라는 말이 아니라, 몇 벌까지 감당할 수 있는지 스스로 셈해보라는 쪽에 가깝습니다.

⚖️ 자기 비중을 숫자로 말한다는 것

마지막 문단이 이 글에서 가장 성숙한 부분이라고 봤습니다.

"나는 품질과 속도를 놓고 봤을 때 3.5:6.5 정도로 비중을 두고 있는 것 같다. 분명히 품질이 올라야 할 때가 올 것이고, 그때에는 composition과 함께 글을 써야겠다."

세 가지가 들어 있어요.

첫째, 자기 기준을 숫자로 꺼냈습니다. "상황에 따라 다르다"로 끝내지 않았어요.

둘째, 그게 영구적이지 않다고 못박았습니다. 지금 국면의 선택이라는 걸 알고 있습니다.

셋째, 바뀔 조건과 그때의 대안까지 적어뒀습니다. composition 패턴이라는 이름까지 붙여서요.

이렇게 써두면 나중에 자기 글을 다시 읽었을 때 "그때 나는 왜 그랬나"가 복원됩니다. 결론만 남긴 기록과 조건까지 남긴 기록의 차이가 여기서 갈려요.

큐레이터 노트

정답을 말하지 않고 자기 기준을 말한 글이라 뽑았습니다.

"컴포넌트를 빼야 하나 말아야 하나"는 답이 정해진 문제가 아닙니다. 그래서 대부분 "상황에 따라 다르다"로 끝나요. 그런데 이 글은 자기가 실제로 어느 쪽을 기본값으로 두는지, 왜 그렇게 됐는지, 언제 바꿀 건지를 말합니다.

"물론 리뷰에 따라 기각당할 수 있지만" 같은 괄호도 좋았어요. 자기 방식을 절대화하지 않으면서도 분명하게 말하는 톤입니다.

한계를 스스로 밝힌 것도 신뢰가 갔습니다. "저 UI 안에 고유한 상태를 만들 수 없다는 한계를 가진다"고 적고, 그런데 그 경우엔 오히려 리팩토링이 쉬웠다고 이어가요. 약점을 감추지 않고, 그 약점이 실제로 얼마나 아팠는지까지 말합니다.

무엇보다 "내 머릿속에는 어떤 게 무의식 속에 남아 이 작업을 하고 있을까?"라는 질문에서 출발한 글입니다. 새로 배운 걸 정리한 게 아니라 이미 하고 있던 걸 꺼내 본 기록이에요. 성장일지가 특히 잘 맞는 형태입니다.

원문 읽으러 가기  ↗semyung.dev/blogs/my-think-of-rendering-list/

💡 두 편을 겹쳐 읽으며

두 글의 주제는 꽤 멀어 보입니다. 하나는 HTML 태그 고르기, 하나는 리액트 컴포넌트 구조.

그런데 나란히 놓으면 같은 질문 위에 서 있습니다. "화면이 똑같은데 왜 구조를 고민하나."

<div>로 짜도 화면은 같습니다. <Item />으로 빼도 화면은 같아요. 사용자가 보는 결과만 놓고 보면 두 선택 다 차이가 없습니다.

그런데 둘 다 고민할 이유를 댑니다. 그리고 그 이유가 화면 말고 다른 걸 보는 독자라는 점에서 겹쳐요.

진현주 님의 독자는 스크린 리더와 검색 크롤러입니다. 눈으로 화면을 보지 않고 태그 구조를 읽는 존재들이죠. 그들에게는 <div><nav>가 완전히 다른 문서입니다.

오세명 님의 독자는 LLM과 몇 달 뒤의 자신입니다. 실행 결과가 아니라 코드를 읽는 쪽이에요. 그들에게는 파일이 하나인지 둘인지가 실제로 다른 경험입니다.

독자를 특정하면 구조가 정해집니다. 반대로 독자를 정하지 않으면 "그때그때 편한 대로"가 되고, 그 결과가 <div> 더미이거나 물음표가 잔뜩 붙은 Props입니다.

두 글 다 자기가 누구를 위해 쓰는지 알고 있었어요.

이번 회차에서 가져갈 것

1. 시맨틱 태그는 랜드마크를 만든다.

스크린 리더 사용자는 구역 단위로 건너뛰며 페이지를 씁니다. <main>, <nav>가 없으면 그 이동이 불가능해져요. <div>만 쓴 페이지는 목차 없는 책입니다.

2. role을 붙이기 전에 태그부터 확인하기.

<div role="navigation">보다 <nav>가 낫습니다. 접근성 표준의 원칙이 "맞는 HTML 요소가 있으면 그것을 먼저 쓰라"예요.

3. <details><time>은 공짜로 받는 게 많다.

아코디언을 직접 만들면 키보드 접근성과 상태 안내를 따로 챙겨야 합니다. <details>는 그게 기본으로 들어 있어요. <time datetime="...">은 사람과 기계에게 각각 맞는 형태를 동시에 줍니다.

4. Props에 물음표가 늘어나면 신호다.

optional이 하나 붙을 때마다 경우의 수가 두 배가 됩니다. 넷이면 열여섯 가지예요. 그 컴포넌트가 실제로 몇 가지 화면을 그리는지 모르게 되면 추출이 이득을 잃은 상태입니다.

5. 조건 계산은 위로 몰고, 반환문은 배치만.

map 안에서 const button = 조건 ? <button/> : null로 먼저 계산해두면 반환문이 조용해집니다. 화면 구조가 한눈에 들어와요.

6. "겉모습이 같다"와 "같은 것이다"는 다르다.

지금 똑같아 보이는 UI라도 각자 자기 맥락으로 갈라집니다. 합쳐두면 그때부터 optional이 붙기 시작해요. 감당 가능한 수준에서는 중복이 나을 수 있습니다.

7. 자기 기준을 숫자와 조건으로 남기기.

"상황에 따라 다르다"는 나중에 아무것도 복원해주지 않습니다. 지금 어느 쪽에 얼마나 기울어 있는지, 언제 바꿀 건지까지 적어두면 그게 나중의 나에게 남는 기록이 돼요.


좋은 글 남겨주신 진현주 님, 오세명 님께 감사드립니다! 😊

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

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


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

WRITE YOUR GROWTH

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

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

커뮤니티 둘러보기