Skip to content

모바일 반응형 레이아웃 및 Tab 필터링 기능 구현#11

Merged
Dobbymin merged 17 commits intomainfrom
fix#10-tag-mobile
Oct 25, 2025
Merged

모바일 반응형 레이아웃 및 Tab 필터링 기능 구현#11
Dobbymin merged 17 commits intomainfrom
fix#10-tag-mobile

Conversation

@Dobbymin
Copy link
Copy Markdown
Owner

📝 상세 내용

모바일 환경 최적화 및 Tab 기능 개선

모바일 환경에서 레이아웃이 깨지는 문제를 해결하고, Tab 컴포넌트에 동적 필터링 기능을 추가했습니다.
또한 FSD 아키텍처에 맞게 컴포넌트 구조를 리팩토링했습니다.

🎯 주요 구현 사항

1. Tab 컴포넌트 전면 개선

모바일 레이아웃 수정

  • shrink-0, whitespace-nowrap로 탭 줄바꿈 방지
  • overflow-x-auto로 가로 스크롤 활성화
  • 반응형 패딩: px-4 md:px-5
  • Grid와 동일한 최대 너비: max-w-7xl px-4 md:px-8

동적 카테고리 기능

  • 자동 카테고리 추출: 게시글 데이터에서 카테고리 자동 수집
  • 실시간 카운트: 각 카테고리별 게시글 개수 표시
  • All 탭: 전체 게시글 보기
  • 필터링: 선택한 카테고리에 따라 게시글 필터링
// 임시 데이터 제거
const tabs = [
  { name: 'All', count: posts.length, category: null },
  ...Object.entries(categoryCounts).map(([category, count]) => ({
    name: category,
    count,
    category,
  })),
];

2. Header 모바일 최적화

텍스트 크기 조정

  • 로고: text-xl md:text-3xl (20px → 30px)
  • About: text-base md:text-xl (16px → 20px)

로고 표시 방식

  • 모바일: 로고 이미지만 표시
  • 데스크톱: "Dobblog" 텍스트 + 로고 함께 표시

레이아웃 구조 개선

<header> {/* 외부: 전체 너비 배경 */}
  <div className='max-w-7xl px-4 md:px-8'> {/* 내부: 제한된 너비 */}
    {/* 콘텐츠 */}
  </div>
</header>

3. Footer 반응형 구현

모바일 환경

표시 항목:

숨김 항목:

  • 전화번호

스타일 개선

  • text-sm md:text-base: 반응형 텍스트 크기
  • flex-wrap: 줄바꿈 지원
  • px-4: 좌우 패딩 추가

4. 전역 레이아웃 문제 해결

globals.css 수정

/* 기존 */
body {
  @apply flex items-center justify-center bg-blog-gray-100 font-pretendard;
}

/* 수정 */
html, body {
  @apply overflow-x-hidden;
}
body {
  @apply bg-blog-gray-100 font-pretendard;
}

문제: body에 justify-center가 있어서 콘텐츠가 중앙 정렬되면서 모바일에서 오른쪽 여백 발생

해결: 불필요한 flex 레이아웃 제거 및 overflow-x-hidden 추가

5. PostCard 간격 문제 해결

Grid 컴포넌트 수정

// 기존: 동적 클래스 (작동 안 함)
<Grid gap={5} />

// 수정: 직접 명시
<Grid className='gap-5' />

원인: Tailwind CSS는 빌드 타임에 클래스를 스캔하므로 동적으로 생성된 클래스명(gap-${gap})을 인식하지 못함

6. FSD 아키텍처 리팩토링

PostList 컴포넌트 이동

❌ 기존: src/widgets/post/PostList.tsx
✅ 수정: src/features/main/ui/PostList.tsx

이유:

  • widgets: 재사용 가능한 UI 조각 (Grid, Header, Footer)
  • features: 비즈니스 로직 + 상태 관리 (PostList의 필터링 기능)

서버/클라이언트 컴포넌트 분리

  • app/page.tsx: 서버 컴포넌트 (데이터 로드)
  • features/main/ui/PostList.tsx: 클라이언트 컴포넌트 (상태 관리)

7. PostCard 높이 조정

  • min-h-[300px]: 최소 높이 설정
  • h-full: 그리드 내 균일한 높이
  • p-3 → p-4: 콘텐츠 영역 패딩 증가
  • line-clamp-3: description 3줄 초과 시 말줄임표

🏗️ 기술 스택 및 아키텍처

반응형 디자인 전략

일관된 패딩 시스템:

  • 모바일: px-4 (1rem / 16px)
  • 태블릿: md:px-8 (2rem / 32px)
  • 데스크톱: lg:px-0 + max-w-7xl

적용 컴포넌트:

  • Header
  • Tabs
  • Grid (PostCard)
  • Footer

FSD 아키텍처

src/
├── app/page.tsx                    # 라우팅 + 서버 데이터 로드
├── features/
│   └── main/ui/PostList.tsx       # 메인 페이지 기능 (상태 + 필터링)
├── shared/
│   └── components/features/
│       ├── tabs/Tabs.tsx           # Tab 컴포넌트 (동적 카테고리)
│       ├── card/PostCard.tsx       # 게시글 카드
│       └── layout/
│           ├── Header.tsx          # 헤더 (반응형)
│           └── Footer.tsx          # 푸터 (반응형)
└── widgets/
    ├── grid/Grid.tsx               # 그리드 위젯
    └── layout/BlogLayout.tsx       # 레이아웃 래퍼

🐛 해결된 버그

1. 모바일 레이아웃 오버플로우

  • 원인: body의 justify-center + 컴포넌트 고정 너비
  • 해결: overflow-x-hidden + 일관된 패딩 시스템

2. Tab 너비 불일치

  • 원인: Tab과 Grid의 패딩/너비가 달랐음
  • 해결: 동일한 max-w-7xl px-4 md:px-8 lg:px-0 적용

3. PostCard 간격 없음

  • 원인: Tailwind 동적 클래스 미인식
  • 해결: classNamegap-5 직접 명시

4. Header 오버플로우

  • 원인: 외부 컨테이너에 최대 너비 제한 없음
  • 해결: 내부 div에 max-w-7xl + relative 포지셔닝

✨ 주요 특징

✅ 장점

  1. 완벽한 반응형: 모바일/태블릿/데스크톱 모두 최적화
  2. 동적 Tab 시스템: MDX 파일 추가 시 자동으로 카테고리 생성
  3. 일관된 디자인: 모든 컴포넌트가 동일한 패딩/너비 시스템 사용
  4. FSD 준수: 비즈니스 로직과 UI 컴포넌트 명확히 분리
  5. 타입 안정성: TypeScript로 props 타입 정의
  6. 접근성 향상: 모바일에서 핵심 정보만 표시

⚠️ 주의사항

  1. Tailwind 동적 클래스는 직접 명시해야 함
  2. overflow-x-hidden은 html/body에 모두 적용
  3. Header 검색창은 relative 부모 기준 absolute 포지셔닝
  4. PostList는 클라이언트 컴포넌트 (상태 관리 필요)

📊 변경 파일 목록

새로 생성된 파일

  • src/features/main/ui/PostList.tsx
  • src/features/main/ui/index.ts
  • src/features/main/index.ts

수정된 파일

  • src/shared/components/features/tabs/Tabs.tsx
  • src/shared/components/features/layout/Header.tsx
  • src/shared/components/features/layout/Footer.tsx
  • src/shared/components/features/card/PostCard.tsx
  • src/app/page.tsx
  • src/app/globals.css
  • src/features/index.ts
  • src/widgets/index.ts

삭제된 파일

  • src/widgets/post/PostList.tsx
  • src/widgets/post/index.ts

#️⃣ 이슈 번호

⏰ 현재 버그

X

📷 주요 변경사항

1. Tab 컴포넌트

  • 모바일에서 가로 스크롤 가능
  • 실제 게시글 데이터 기반 동적 카테고리
  • All/Develop 등 카테고리별 필터링

2. Header

  • 모바일: 로고만 표시
  • 데스크톱: 텍스트 + 로고
  • 모든 화면 크기에서 중앙 정렬 및 최대 너비 제한

3. Footer

  • 모바일: 필수 정보만 (Copyright, 이메일, GitHub, LinkedIn)
  • 데스크톱: 전체 정보 (전화번호 포함)
  • 반응형 텍스트 크기

4. PostCard

  • 최소 높이 300px
  • 카드 간 일정한 간격 (gap-5)
  • 그리드 내 균일한 높이

5. 전체 레이아웃

  • 오른쪽 공백 제거
  • 일관된 패딩 시스템
  • 가로 스크롤 방지

스크린샷 (모바일 화면)

image

@Dobbymin Dobbymin self-assigned this Oct 25, 2025
@Dobbymin Dobbymin added 😡 Bug Fix 발생한 버그를 수정하는 경우 🎨 Design UI / UX 디자인 관련 작업을 진행하는 경우 labels Oct 25, 2025
@Dobbymin Dobbymin linked an issue Oct 25, 2025 that may be closed by this pull request
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @Dobbymin, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 모바일 환경에서의 사용자 경험을 대폭 개선하고, Tab 컴포넌트에 동적인 필터링 기능을 추가하며, FSD 아키텍처 원칙에 따라 컴포넌트 구조를 리팩토링하는 것을 목표로 합니다. 전반적인 레이아웃 문제를 해결하고, 반응형 디자인을 강화하여 블로그의 접근성과 유지보수성을 향상시켰습니다.

Highlights

  • 모바일 반응형 레이아웃 개선: Header, Footer, Tab 컴포넌트 및 전역 레이아웃에 걸쳐 모바일 환경에서의 레이아웃 깨짐 현상을 해결하고, 일관된 반응형 패딩 시스템을 적용하여 모든 기기에서 최적화된 사용자 경험을 제공합니다.
  • Tab 컴포넌트 동적 필터링 기능 구현: 게시글 데이터에서 카테고리를 자동으로 추출하고, 각 카테고리별 게시글 수를 실시간으로 표시하며, 선택된 카테고리에 따라 게시글을 필터링하는 기능을 추가했습니다. 모바일 환경에서 탭이 줄바꿈되지 않고 가로 스크롤이 가능하도록 개선되었습니다.
  • FSD 아키텍처 리팩토링: PostList 컴포넌트를 widgets에서 features로 이동시키고, 서버 컴포넌트(app/page.tsx)와 클라이언트 컴포넌트(features/main/ui/PostList.tsx)를 명확히 분리하여 비즈니스 로직과 UI 컴포넌트의 역할을 분리했습니다.
  • 전역 레이아웃 및 PostCard 간격 문제 해결: globals.css에서 body의 불필요한 flex 속성을 제거하고 overflow-x-hidden을 추가하여 모바일 오버플로우를 해결했습니다. 또한, Tailwind CSS의 동적 클래스 인식 문제를 해결하기 위해 PostCardgap 속성을 직접 명시하여 카드 간 간격을 조정했습니다.
  • PostCard 높이 및 스타일 조정: PostCard에 최소 높이(min-h-[300px])를 설정하고, 그리드 내에서 균일한 높이를 유지하도록 h-full을 적용했습니다. 또한, 콘텐츠 패딩을 늘리고 description이 3줄을 초과할 경우 말줄임표를 표시하도록 개선했습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

모바일 반응형 레이아웃을 적용하고 Tab 필터링 기능을 구현하는 등 사용자 경험을 크게 개선한 PR입니다. FSD 아키텍처에 따라 컴포넌트 구조를 리팩토링하고, 반응형 디자인 원칙을 일관되게 적용한 점이 훌륭합니다. 특히 PostList 컴포넌트를 features 레이어로 이동하여 관심사를 분리한 것은 좋은 설계 결정입니다.

코드 리뷰에서는 주로 성능 최적화에 대한 몇 가지 제안을 드렸습니다. PostListTabs 컴포넌트에서 매 렌더링 시 실행되는 데이터 처리 로직에 useMemo를 적용하여 불필요한 계산을 방지하도록 수정하는 것을 권장합니다. 이를 통해 애플리케이션의 반응성을 더욱 향상시킬 수 있을 것입니다. 전체적으로 훌륭한 작업입니다!

Comment on lines +18 to +20
const filteredPosts = selectedCategory
? posts.filter((post) => post.category === selectedCategory)
: posts;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

filteredPosts 배열이 렌더링될 때마다 새로 생성되고 있습니다. 게시물 수가 많아지면 성능에 영향을 줄 수 있습니다. useMemo 훅을 사용하여 postsselectedCategory가 변경될 때만 필터링 로직이 다시 실행되도록 최적화하는 것을 권장합니다.

이를 위해 파일 상단에 useMemo를 import해야 합니다: import { useMemo, useState } from 'react';

  const filteredPosts = useMemo(() => {
    return selectedCategory
      ? posts.filter((post) => post.category === selectedCategory)
      : posts;
  }, [posts, selectedCategory]);

Comment on lines 15 to 37
// 게시글에서 카테고리 추출 및 카운트
const getCategoryCounts = () => {
const categoryCounts: Record<string, number> = {};

posts.forEach((post) => {
const category = post.category;
categoryCounts[category] = (categoryCounts[category] || 0) + 1;
});

return categoryCounts;
};

const categoryCounts = getCategoryCounts();

// All 탭 + 각 카테고리 탭 생성
const tabs = [
{ name: 'All.tsx', count: 49 },
{ name: 'Develop.tsx', count: 3 },
{ name: 'Daily.tsx', count: 9 },
{ name: 'Review.tsx', count: 4 },
{ name: 'All', count: posts.length, category: null },
...Object.entries(categoryCounts).map(([category, count]) => ({
name: category,
count,
category,
})),
];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

tabs 배열이 렌더링될 때마다 재생성되고 있습니다. 게시물 수가 많을 경우 성능 저하의 원인이 될 수 있습니다. useMemo를 사용하여 posts prop이 변경될 때만 tabs 배열을 다시 계산하도록 메모이제이션하는 것이 좋습니다.

추가로, getCategoryCounts 함수 내부의 forEach 대신 reduce를 사용하면 코드를 더 간결하게 만들 수 있습니다.

이를 위해 파일 상단에 useMemo를 import해야 합니다: import { useMemo, useState } from 'react';

Suggested change
// 게시글에서 카테고리 추출 및 카운트
const getCategoryCounts = () => {
const categoryCounts: Record<string, number> = {};
posts.forEach((post) => {
const category = post.category;
categoryCounts[category] = (categoryCounts[category] || 0) + 1;
});
return categoryCounts;
};
const categoryCounts = getCategoryCounts();
// All 탭 + 각 카테고리 탭 생성
const tabs = [
{ name: 'All.tsx', count: 49 },
{ name: 'Develop.tsx', count: 3 },
{ name: 'Daily.tsx', count: 9 },
{ name: 'Review.tsx', count: 4 },
{ name: 'All', count: posts.length, category: null },
...Object.entries(categoryCounts).map(([category, count]) => ({
name: category,
count,
category,
})),
];
const tabs = useMemo(() => {
const categoryCounts = posts.reduce<Record<string, number>>((acc, { category }) => {
acc[category] = (acc[category] || 0) + 1;
return acc;
}, {});
return [
{ name: 'All', count: posts.length, category: null },
...Object.entries(categoryCounts).map(([category, count]) => ({
name: category,
count,
category,
})),
];
}, [posts]);

@Dobbymin Dobbymin merged commit 2d1e95a into main Oct 25, 2025
1 check passed
@Dobbymin Dobbymin deleted the fix#10-tag-mobile branch October 25, 2025 16:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

😡 Bug Fix 발생한 버그를 수정하는 경우 🎨 Design UI / UX 디자인 관련 작업을 진행하는 경우

Projects

None yet

Development

Successfully merging this pull request may close these issues.

태그 - 모바일 환경 수정

1 participant