Skip to content

Style: UI 수정#11

Merged
Monixc merged 23 commits into
developfrom
feature/ui-edit
Aug 7, 2025
Merged

Style: UI 수정#11
Monixc merged 23 commits into
developfrom
feature/ui-edit

Conversation

@Monixc

@Monixc Monixc commented Aug 4, 2025

Copy link
Copy Markdown
Contributor

📌 PR 종류 (PR Type)

  • 🐛 버그 수정 (Bugfix)
  • ✨ 기능 추가 (Feature)
  • 🎨 UI 스타일 변경 (Style) - CSS, 클래스명, 레이아웃 등 UI 변경
  • ♻️ 코드 리팩토링 (Refactor) - 기능 변화 없이 코드 구조 개선
  • 📦 패키지/빌드 관련 변경 (Build) - 설정 파일 수정, 의존성 추가 등
  • 🧱 테스트 코드 추가/수정 (Test)
  • 📝 문서 수정 (Docs)
  • 🚧 WIP (작업 중 PR)
  • 기타 (Other):

📝 요약 (Summary)

ex) 게시글 리스트 UI 개선 및 반응형 대응

글로벌 레이아웃 구조 개선 및 페이지 UI 개선


🔍 상세 내용 (Describe your changes)

ex)

글로벌 레이아웃 구조 개선

  • header을 페이지별로 import 해서 사용하게 수정했습니다. bookmark, notification, search, like 등 페이지별로 header에서 처리하는 기능이 달라, GlobalLayout에서 분리하여 사용하도록 변경했습니다.
  • shouldShowBottomNav 배열에 입력된 경로는 BottomNavigation 컴포넌트를 사용합니다. 기존 코드에서 페이지마다 GlobalLayout을 최상위 래퍼로 호출하고, showBottomNav 등으로 헤더/푸터 표시 여부를 설정하는 구조가 비효율적이라고 생각하여 GlobalLayout에서 일괄로 처리하도록 변경하였습니다.
  • 글로벌 레이아웃에서 적용중이던 p-4를 삭제하였습니다. 페이지에서 <div className=p-4> 로 요소들을 감싸 사용합니다.

헤더 컴포넌트 추가

  • 헤더 좌, 우 아이콘 커스텀 가능하도록 수정
  • 헤더가 필요한 페이지에서 import 해서 사용

바텀 네비게이션바 페이지 이동 기능 구현

  • 각 아이콘 클릭 시 해당하는 페이지로 이동

온보딩 페이지 UI 개선

  • tailwind 컬러 시스템(bg-gray-400 등) -> brand- 컬러 사용하도록 변경하였습니다.

게시글 상세 페이지 UI 개선

  • 전체 레이아웃 수정
  • 입력부 textarea-> input으로 변경
  • 댓글, 답글 들여쓰기로 구분해서 보이도록 변경
  • msw 핸들러 댓글, 답글, 좋아요 관련 코드 추가, board에서 게시글 클릭 시 해당 community/:postId 경로로 이동하는 기능 추가

검색 페이지 UI 개선

  • input 스타일 수정
  • 전체적으로 정렬된 느낌이 들도록 UI 수정했습니다.

📸 스크린샷 (필수)

Before After
image image
image image
image image

✅ 확인 사항 (CheckList)

  • 브라우저에서 정상 동작 확인
  • 기존 기능에 영향 없음
  • 반응형 UI 대응 확인
  • 코드 컨벤션에 맞게 작성됨 (eslint, prettier 등)
  • 테스트 코드 추가/수정 또는 테스트 통과 확인

💬 기타 참고사항 (Optional)

ex) 이 부분 로직이 좀 복잡해서 한번 더 확인 부탁드립니다 🙏

Monixc added 15 commits August 3, 2025 20:55
- 페이지 별 레이아웃 관리를 app.tsx -> globalLayout에서 담당하도록 변경
- 기존에 globalLayout에서 처리하던 header를 컴포넌트로 분리, 페이지에서 import 하여 사용하도록 변경
- header에서 bookmark, like 등 일부 기능을 처리해야 해서 컴포넌트 분리 후 페이지에서 import 하는 방식 선택
- bottomnavigation 아이콘 클릭 시 해당 페이지로 이동
- RecentSearchList 및 SearchHeader 컴포넌트를 _components 폴더로 이동
- RecentSearchList 삭제 아이콘 p-0 적용하여 정렬 스타일 개선
- SearchHeader 컴포넌트 스타일 수정
- 페이지 별 헤더 및 헤더 아이콘 적용
- header을 페이지에서 관리함에 따라 GlobalLayout에서 p 적용 시 header에도 패딩 적용됨
- GlobalLayout p-4 속성 삭제 후, 각 페이지 콘텐트 div에서 패딩 적용
- 버튼 컴포넌트의 스타일을 통일성 있게 수정
- 입력 필드 및 선택기 스타일 개선
- 관심 분야 선택 제한 로직 추가
- PostDetailPage에서 댓글 및 답글 좋아요 기능을 추가
- CommentItem 및 CommentSection 컴포넌트에서 답글 좋아요 기능 통합
- PostContent 및 CommentSection의 UI 개선
- ReplyItem 컴포넌트 새로 추가하여 답글 표시 및 좋아요 기능 구현
- 세미콜론 추가로 코드 일관성 유지
- 불필요한 공백 제거
- ListItem 컴포넌트에 클릭 시 해당 포스트의 커뮤니티 경로로 이동하는 기능 추가
- useNavigate 훅을 사용하여 포스트 ID에 기반한 경로 설정
- Reply 인터페이스를 추가하여 답글 구조 정의
- Comment 인터페이스에 isLiked 및 replyCount, replies 필드 추가
- 커뮤니티 게시글 상세 조회, 댓글 목록 조회, 댓글 추가, 좋아요 토글 기능 구현
- 게시글 및 댓글 데이터 구조 정의 및 초기 데이터 추가
- 답글 추가 및 좋아요 기능을 위한 API 핸들러 추가
- 게시글 및 댓글 데이터 가져오는 로직을 간소화하여 가독성 향상
- 댓글 좋아요 기능을 위한 toggleCommentLike 훅 추가
- 답글 좋아요 기능을 위한 toggleReplyLike 훅 추가
- 에러 메시지 문자열을 일관된 쌍따옴표로 변경
@github-actions github-actions Bot added ✨feature 기능 개발 🎨style 스타일 변경 🐞bugfix 버그 수정 🔄refactor 코드 리팩토링 labels Aug 4, 2025
@Monixc Monixc requested review from ksy90101 and woo-dev-log August 4, 2025 03:48

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Summary of Changes

Hello @Monixc, 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!

This pull request delivers a UI/UX overhaul, focusing on improving the application's overall look and feel, enhancing user interaction, and streamlining the global layout management. It introduces a more modular header system, activates the bottom navigation, and refines key user flows like community post viewing and onboarding, all while ensuring a consistent visual design.

Highlights

  • Global Layout Refactor: The GlobalLayout component no longer manages header visibility, allowing pages to import and configure their own Header components. Bottom navigation visibility is now dynamically controlled within GlobalLayout based on the current route. Global padding has also been removed, giving individual pages more control over their spacing.
  • Customizable Header Component: A new Header component accepts leftIcon, rightIcon, onLeftClick, and onRightClick props, enabling different pages to display specific icons and actions in their headers, such as back buttons, search, or notifications.
  • Functional Bottom Navigation: The bottom navigation bar is now fully functional, with icons linked to their respective routes (/board, /explore, /chat, /my), and the active tab is correctly highlighted based on the current URL.
  • Community Post Detail Page Overhaul: The UI for the community post detail page has been revamped, including a new overall layout, changing the comment input field from a textarea to an input, and visually distinguishing comments/replies with indentation. Functionality for liking comments and replies has also been added.
  • Onboarding & Search Page UI Improvements: The onboarding flow's UI has been updated to use the brand- color system. The search page's input field and recent search list have received styling improvements.
  • MSW Mock API Expansion: The mock service worker (MSW) handlers have been extended to support the new community post detail features, including mock endpoints for fetching individual posts, their comments and replies, and handling like toggles for posts, comments, and replies.
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 in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

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 issue 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 or fill out our survey to provide feedback.

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.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

전반적으로 UI 개선 및 리팩토링이 잘 이루어졌습니다. 특히 GlobalLayoutHeader 컴포넌트의 리팩토링으로 코드 구조가 더 명확하고 재사용성이 높아졌습니다. 몇 가지 개선점을 제안드렸으며, 특히 usePost 훅의 쿼리 무효화 로직과 PostContent의 좋아요 버튼 처리 부분을 확인해주시면 좋겠습니다.

또한, 스타일 가이드에 따라 모든 코드에 대한 자동화 테스트가 필요합니다.[^1] 이번 PR에 테스트 코드 변경사항이 포함되지 않은 것으로 보이는데, 추가된 기능과 로직에 대한 테스트 코드를 보강해주시면 코드의 안정성을 더욱 높일 수 있을 것입니다.

Comment thread src/hooks/usePost.ts
Comment on lines +71 to +80
export function useToggleCommentLike() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: toggleCommentLike,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["post"] });
},
});
} No newline at end of file
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

onSuccess에서 ['post'] 키로 쿼리를 무효화하면, 현재 보고 있지 않은 다른 게시물들의 캐시까지 함께 무효화되어 불필요한 데이터 리페칭이 발생할 수 있습니다. postId를 훅의 인자로 받아 ['post', postId]와 같이 더 구체적인 키를 사용해 무효화하는 것이 성능상 유리합니다. 이 변경은 PostDetailPage.tsx에서 훅을 호출하는 부분의 수정도 필요합니다.

Suggested change
export function useToggleCommentLike() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: toggleCommentLike,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["post"] });
},
});
}
\ No newline at end of file
}
export function useToggleCommentLike(postId: string) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: toggleCommentLike,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["post", postId] });
},
});
}

Comment thread src/hooks/usePost.ts
Comment on lines +88 to +96
export function useToggleReplyLike() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: toggleReplyLike,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["post"] });
},
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

useToggleCommentLike와 동일한 문제입니다. postId를 훅의 인자로 받아 ['post', postId] 키를 무효화하여 불필요한 리페칭을 방지하는 것이 좋습니다. 이 변경은 PostDetailPage.tsx에서 훅을 호출하는 부분의 수정도 필요합니다.

Suggested change
export function useToggleReplyLike() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: toggleReplyLike,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["post"] });
},
});
}
export function useToggleReplyLike(postId: string) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: toggleReplyLike,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["post", postId] });
},
});
}

Comment on lines +13 to 58
export function PostContent({ post, onLikeClick }: PostContentProps) {
return (
<main className="flex flex-col gap-y-4 text-white">
<Badge
variant="outline"
className="w-fit border-brand-primary text-brand-primary"
>
{post.category}
</Badge>
<h2 className="text-2xl font-bold">{post.title}</h2>
<div className="flex items-center gap-x-2 text-sm text-gray-400">
<span>{post.author.name}</span>
<Separator orientation="vertical" className="h-3" />
<span>{post.createdAt}</span>
<main className="w-full flex flex-col text-white">
<div className="p-4 flex flex-col gap-y-4">
<Badge
variant="outline"
className="w-fit border-brand-primary text-brand-primary">
{post.category}
</Badge>
<h2 className="text-xl font-bold">{post.title}</h2>
<div className="flex items-center gap-x-2 text-xs text-brand-text">
<span>{post.author.name}</span>
<span>•</span>
<span>{post.createdAt}</span>
</div>
</div>
<Separator className="my-2 bg-gray-700" />
<div className="min-h-[200px] py-4 text-base whitespace-pre-wrap">
{post.content}
</div>
<div className="flex items-center gap-x-2 py-2">
<Button
variant="ghost"
size="icon"
className="h-8 w-8 hover:bg-white/10 rounded-full"
onClick={onLikeClick}
disabled={isLikePending}
>
<Heart
className="h-6 w-6"
fill={post.isLiked ? "#FF4A4A" : "none"}
stroke={post.isLiked ? "#FF4A4A" : "currentColor"}
/>
</Button>
<span className="text-sm font-semibold">{post.likeCount}</span>

<Separator className="w-full bg-brand-surface " />

<div className="p-4 flex flex-col ">
<div className="min-h-[200px] text-sm leading-relaxed whitespace-pre-wrap break-words">
{post.content}
</div>
<div className="flex items-center">
<Button
variant="ghost"
size="icon"
className="h-12 w-12 hover:bg-white/10 rounded-full -ml-2"
onClick={onLikeClick}>
<Heart
className="!h-6 !w-6"
fill={post.isLiked ? "#FF4A4A" : "none"}
stroke={post.isLiked ? "#FF4A4A" : "currentColor"}
/>
</Button>

<span className="text-xs font-semibold flex items-center -mt-0.5">
{post.likeCount}
</span>
</div>
</div>
<Separator className="bg-gray-700 h-1" />

<Separator className="bg-brand-surface !h-1" />
</main>
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

PostContentProps에는 isLikePending prop이 정의되어 있지만, 컴포넌트에서는 사용되지 않고 있습니다. 이로 인해 '좋아요' 버튼이 로딩 중에도 계속 클릭 가능한 상태로 남아있어 중복 요청이 발생할 수 있습니다. isLikePending prop을 다시 사용하고 버튼의 disabled 속성에 바인딩하는 것을 권장합니다.

export function PostContent({ post, onLikeClick, isLikePending }: PostContentProps) {
  return (
    <main className="w-full flex flex-col text-white">
      <div className="p-4 flex flex-col gap-y-4">
        <Badge
          variant="outline"
          className="w-fit border-brand-primary text-brand-primary">
          {post.category}
        </Badge>
        <h2 className="text-xl font-bold">{post.title}</h2>
        <div className="flex items-center gap-x-2 text-xs text-brand-text">
          <span>{post.author.name}</span>
          <span></span>
          <span>{post.createdAt}</span>
        </div>
      </div>

      <Separator className="w-full bg-brand-surface " />

      <div className="p-4 flex flex-col ">
        <div className="min-h-[200px] text-sm leading-relaxed whitespace-pre-wrap break-words">
          {post.content}
        </div>
        <div className="flex items-center">
          <Button
            variant="ghost"
            size="icon"
            className="h-12 w-12 hover:bg-white/10 rounded-full -ml-2"
            onClick={onLikeClick}
            disabled={isLikePending}>
            <Heart
              className="!h-6 !w-6"
              fill={post.isLiked ? "#FF4A4A" : "none"}
              stroke={post.isLiked ? "#FF4A4A" : "currentColor"}
            />
          </Button>

          <span className="text-xs font-semibold flex items-center -mt-0.5">
            {post.likeCount}
          </span>
        </div>
      </div>

      <Separator className="bg-brand-surface !h-1" />
    </main>
  );
}

Comment on lines +12 to +14
const shouldShowBottomNav = ["/board", "/explore", "/chat", "/my"].some(
(path) => location.pathname.startsWith(path)
);

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

shouldShowBottomNav를 결정하는 경로가 하드코딩되어 있습니다. src/constants/navigation.tsNAV_ITEMS를 사용하도록 리팩토링하면 중복을 줄이고 내비게이션 경로 변경 시 더 유연하게 대처할 수 있습니다.

예를 들어, 다음과 같이 수정할 수 있습니다:

// import { NAV_ITEMS } from "@/constants/navigation";

const shouldShowBottomNav = NAV_ITEMS.some((item) =>
  location.pathname.startsWith(item.path)
);

@woo-dev-log woo-dev-log left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM👍

Monixc added 2 commits August 6, 2025 21:29
- AddPostModal 컴포넌트와 PostItem, RecruitItem 컴포넌트를 추가하여 게시글 작성 UI 구성
- useAddPostModal 훅을 통해 상태 관리 및 입력 처리 로직 구현
- 기존의 .gitkeep 파일 삭제
@Monixc Monixc merged commit 8020abe into develop Aug 7, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐞bugfix 버그 수정 ✨feature 기능 개발 🔄refactor 코드 리팩토링 🎨style 스타일 변경

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants