Skip to content

[FIX] 채팅방 스크롤이 간헐적으로 하단까지 이동하지 않는 문제#1316

Merged
dlsxjzld merged 4 commits intodevelopfrom
feature/1314-chat-room-scroll-fix
Feb 28, 2026
Merged

[FIX] 채팅방 스크롤이 간헐적으로 하단까지 이동하지 않는 문제#1316
dlsxjzld merged 4 commits intodevelopfrom
feature/1314-chat-room-scroll-fix

Conversation

@kimyou1102
Copy link
Contributor

@kimyou1102 kimyou1102 commented Feb 15, 2026

Issue Number

closed #1314

As-Is

  • 채팅방 진입 시 초기 스크롤 위치가 간헐적으로 잘못 계산되는 문제
image

To-Be

  • 채팅방 상단 헤더 영역의 높이를 안정화하여, 초기 스크롤 계산이 일관되게 동작하도록 개선

문제 원인

  • 채팅방 상단의 ChatRoomHeader + MentoringActionPanel 영역이 데이터 로딩 전/후에 따라 높이가 변동(622px → 522px)
  • useLayoutEffect 시점에 스크롤을 계산했지만, 이후 헤더 높이가 줄어들면서 clientHeight가 변경
  • 이로 인해 maxScrollTop 값이 달라져 초기 스크롤이 의도와 다르게 되는 문제가 발생

분석 방법

  • ResizeObserver를 활용하여 listRef의 clientHeight 변화를 감지
  • 실제로 헤더 데이터 로딩 이후 리스트 높이가 622px → 522px로 줄어드는 현상을 확인

해결 방법

레이아웃 변동을 제거하기 위해 두 가지 방안을 검토하였습니다.


1️⃣ 스켈레톤 방식

image

(브랜치 받아서 테스트할때는 ChatRoomInfoSkeleton 컴포넌트를 사용하시면 됩니다!)

  • 헤더와 액션 패널의 스켈레톤 UI를 구성하여 로딩 중에도 동일한 높이를 확보

장점

  • 로딩 상태를 시각적으로 표현 가능

단점

  • 실제 컴포넌트와 높이를 정확히 맞춰야 함
  • UI 구조 변경 시 스켈레톤도 함께 수정 필요
  • 로딩 시간이 짧은 화면에서 shimmer 애니메이션은 오히려 UX에 거슬릴 수 있다

2️⃣ 실컴포넌트 렌더 + visibility: hidden 방식 (선택)

image
  • 로딩 중에도 실제 ChatRoomHeaderMentoringActionPanel을 렌더
  • visibility: hidden을 적용하여 레이아웃만 유지
  • 내부 내용만 시각적으로 숨김

장점

  • 실제 컴포넌트와 높이가 100% 동일
  • 구조 변경에도 자동 대응
  • 스켈레톤 관리 비용 제거

단점

  • 로딩 중에도 내부 로직이 일부 실행됨 (이미지 등)
    • 단, 기본 이미지가 로컬 asset이므로 추가 네트워크 부담은 없음

Check List

  • 테스트가 전부 통과되었나요?
  • 모든 commit이 push 되었나요?
  • merge할 branch를 확인했나요?
  • Assignee를 지정했나요?
  • Label을 지정했나요?
  • 닫을 이슈 번호를 지정했나요?

(Optional) Additional Description

이번 이슈의 본질은 “로딩 UX”보다는 레이아웃 안정화를 통한 스크롤 계산의 일관성 확보에 있다고 판단했습니다. 이에 따라 실제 컴포넌트를 렌더한 뒤 visibility: hidden으로 레이아웃만 유지하는 방식을 선택했습니다.
또한 해당 영역은 로딩 상태를 적극적으로 드러내야 하는 리스트 영역과는 짧은 로딩 구간에서 스켈레톤 UI를 노출하는 것보다는 레이아웃만 안정적으로 확보하는 방향이 더 적절하다고 보았습니다.

다른 방식이 더 적절하다고 판단되는 부분이 있다면 의견 주시면 감사하겠습니다!

@kimyou1102 kimyou1102 requested a review from dlsxjzld February 15, 2026 10:32
@kimyou1102 kimyou1102 self-assigned this Feb 15, 2026
@kimyou1102 kimyou1102 added ❄️ FE 프론트엔드 🔧 fix 기능 수정 labels Feb 15, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 15, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/1314-chat-room-scroll-fix

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@dlsxjzld dlsxjzld left a comment

Choose a reason for hiding this comment

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

유경님, 코멘트 남겨주신 내용 정말 잘 읽었습니다!

이 이슈의 본질을 단순한 로딩 UX가 아니라 '초기 스크롤 계산을 위한 레이아웃 안정화' 로 짚어주신 인사이트가 정말 예리하다고 생각합니다. 또한, 짧은 로딩 구간에서 스켈레톤이 번쩍이는 현상이 오히려 시각적으로 거슬릴 수 있다는 점도 깊이 공감합니다. 문제의 핵심과 트레이드오프를 명확히 짚어주셔서 저도 뷰를 넓힐 수 있었어요.

다만, 레이아웃을 확보하기 위해 실제 컴포넌트를 마운트하고 mentorName="", price={0} 같은 더미 데이터를 주입하는 부분은 장기적인 코드 유지보수 관점에서 조금 우려되는 점이 있어 조심스레 의견을 보태봅니다.

당장은 안전하지만, 추후 다른 팀원이 MentoringActionPanel 내부에 DOM을 직접 참조하는 로직(useLayoutEffect)이나 특정 비동기/인터랙션 로직을 추가하게 된다면, 화면에 보이지 않는 섀도우 컴포넌트에서 예기치 못한 에러나 사이드 이펙트가 발생할 잠재적 위험이 될 수 있을 것 같습니다.

💡 제안 사항: CSS min-height + 지연된 스켈레톤(Deferred Skeleton)

유경님이 원하시는 '즉각적인 높이 확보''짧은 로딩 시의 깜빡임 방지' 를 모두 만족하면서도, 구조적으로 더 단단하게 가져갈 수 있는 방법은 어떨까요?

1. 지연 컴포넌트 (DeferredFallback) 구현

빠른 네트워크 환경에서 스켈레톤이 0.1초 만에 나타났다 사라지는 '번쩍임' 현상을 막기 위해, 일정 시간(예: 200ms) 동안은 렌더링을 지연시키는 유틸리티 컴포넌트입니다.

interface DeferredFallbackProps {
  delay?: number;
}

function DeferredFallback({
  children,
  delay = 200,
}: PropsWithChildren<DeferredFallbackProps>) {
  const [isDeferred, setIsDeferred] = useState(false);

  useEffect(() => {
    const timeoutId = setTimeout(() => setIsDeferred(true), delay);
    return () => clearTimeout(timeoutId);
  }, [delay]);

  if (!isDeferred) {
    return null;
  }

  return <>{children}</>;
}

2. 실제 적용 (레이아웃 안정화 + 지연 로딩)

DeferredFallback이 200ms 동안 null을 반환할 때 높이가 0이 되어 초기 스크롤 계산이 다시 어긋나는 것을 방지하기 위해, 해당 영역을 감싸는 래퍼(Wrapper)에 예상 높이를 미리 부여합니다.

<S_Container>
  {chatRoomInfoIsPending || !chatRoomInfoData ? (
    // 로딩 중에만 렌더링되는 Wrapper에 예상 높이(예: min-height: 11rem)를 확보하여 스크롤 어긋남 방지
    <S_LoadingHeaderArea> 
      <DeferredFallback delay={200}>
        <ChatRoomInfoSkeleton />
      </DeferredFallback>
    </S_LoadingHeaderArea>
      ) : (
        <div>
          <ChatRoomHeader name={chatRoomInfoData.opponentName} />
          <MentoringActionPanel
            mentorName={chatRoomInfoData.mentorName}
            price={chatRoomInfoData.price}
            profileImageUrl={chatRoomInfoData.profileImageUrl}
            mentorOwned={chatRoomInfoData.myRole === 'MENTOR'}
            onPaymentRequestClick={handlePaymentRequestClick}
            onReviewRequestClick={handleReviewRequestClick}
            onEndClick={handleEndClick}
            onPaymentClick={handlePaymentClick}
            onReviewClick={handleReviewClick}
          />
        </div>
      )}
...

이렇게 하면 빈 껍데기 컴포넌트를 유지하는 부담을 줄이면서도, 유경님이 의도하신 스크롤 계산 버그 해결과 쾌적한 UX를 동시에 달성할 수 있을 것 같습니다.

스켈레톤을 관리해야 하는 비용이 약간 발생하긴 하지만, 컴포넌트의 역할과 책임을 명확히 분리하는 것이 장기적으로 더 유연한 코드가 될 것 같아 의견 남겨봅니다. 이 방식에 대해 어떻게 생각하시나요? 편하게 의견 나누어 주시면 감사하겠습니다! 🙏

@kimyou1102
Copy link
Contributor Author

@dlsxjzld

특정 시간 내에 스켈레톤 렌더링을 지연하는 방식도 고려해보았지만, 현재 구현 수준에서도 기능적으로는 충분하다고 판단하여 적용하지는 않았습니다.

다만,

당장은 안전하지만, 추후 다른 팀원이 MentoringActionPanel 내부에 DOM을 직접 참조하는 로직(useLayoutEffect)이나 특정 비동기/인터랙션 로직을 추가하게 된다면, 화면에 보이지 않는 섀도우 컴포넌트에서 예기치 못한 에러나 사이드 이펙트가 발생할 잠재적 위험이 될 수 있다.

는 의견을 보고, 구조적으로 더 안전한 방향으로 개선하는 것이 맞겠다고 판단하였습니다. 생각지 못한 부분 짚어주셔서 감사합니다 ☺️

추가 제안 사항

Wrapper에 예상 높이를 두는 방식의 경우 반응형 환경에서 줄바꿈이나 레이아웃 변경이 발생하면, 로딩 중 화면과 실제 데이터 렌더링 이후 화면의 높이가 미묘하게 달라질 가능성도 있다고 생각했습니다.

그래서 종욱님의 의견에 더해, 스켈레톤이 실제 레이아웃과 1:1로 맞춰져 있다는 점을 활용하여, Wrapper에 예상 높이를 두기보다는
스켈레톤을 그대로 렌더링하되 일정 시간 이하에는 visibility로 제어하여 레이아웃은 유지하면서도 번쩍임을 방지하는 방식은 어떨까요??

@dlsxjzld
Copy link
Contributor

dlsxjzld commented Feb 23, 2026

추가 제안 사항

Wrapper에 예상 높이를 두는 방식의 경우 반응형 환경에서 줄바꿈이나 레이아웃 변경이 발생하면, 로딩 중 화면과 실제 데이터 렌더링 이후 화면의 높이가 미묘하게 달라질 가능성도 있다고 생각했습니다.

그래서 종욱님의 의견에 더해, 스켈레톤이 실제 레이아웃과 1:1로 맞춰져 있다는 점을 활용하여, Wrapper에 예상 높이를 두기보다는 스켈레톤을 그대로 렌더링하되 일정 시간 이하에는 visibility로 제어하여 레이아웃은 유지하면서도 번쩍임을 방지하는 방식은 어떨까요??

스켈레톤을 그대로 렌더링하되 일정 시간 이하에는 visibility로 제어하는 방식 너무 좋은 것 같습니다 👍👍

다양한 의견을 나누고 배울 수 있어서 좋았습니다 👍

- hidden 상태에서 실제 컴포넌트를 마운트하던 구조 제거
- 로딩 UI와 실제 로직을 분리해 잠재적 사이드이펙트 예방
- 스켈레톤에 delayed visibility를 적용해 로딩 깜빡임 방지
@dlsxjzld dlsxjzld self-requested a review February 28, 2026 07:42
@dlsxjzld dlsxjzld merged commit e61c59a into develop Feb 28, 2026
3 checks passed
@dlsxjzld dlsxjzld deleted the feature/1314-chat-room-scroll-fix branch February 28, 2026 07:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

❄️ FE 프론트엔드 🔧 fix 기능 수정

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[FIX] 채팅방 스크롤이 간헐적으로 하단까지 이동하지 않는 문제

2 participants