Skip to content

fix: QA 2차 수정#172

Merged
heeeeyong merged 6 commits intodevelopfrom
chore/minor-updates
Aug 20, 2025
Merged

fix: QA 2차 수정#172
heeeeyong merged 6 commits intodevelopfrom
chore/minor-updates

Conversation

@heeeeyong
Copy link
Collaborator

@heeeeyong heeeeyong commented Aug 20, 2025

#️⃣연관된 이슈

없음

📝작업 내용

  • 피드쪽 로딩스피너 추가
  • 피드에서 사진간 간격 사라진 문제 해결

Summary by CodeRabbit

  • 신기능

    • 피드 화면 초기 진입 및 탭 전환 시 전체 화면 로딩 스피너를 표시합니다.
    • 마이페이지 진입 시 전체 로딩 스피너와 안내 문구로 프로필 로딩 상태를 보여줍니다.
  • 리팩터

    • 탭별 로딩 흐름을 정리해 스피너 중심으로 가시적 로딩을 통합했습니다.
    • 이미지 렌더링 시 불필요한 래퍼 요소를 제거해 DOM을 단순화했습니다(특정 클래스에 의존한 스타일 영향 가능).
  • 기타 변경

    • 내 피드의 인라인 로딩 스피너 제거(로딩 중 빈 화면 표시).
    • 팔로우 목록은 초기 로딩 완료 전까지 내용을 표시하지 않습니다.
    • Open Graph 이미지 URL이 .png에서 .jpeg로 변경되었습니다.

@heeeeyong heeeeyong self-assigned this Aug 20, 2025
@vercel
Copy link

vercel bot commented Aug 20, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
thip Ready Ready Preview Comment Aug 20, 2025 3:11am

@heeeeyong heeeeyong added the 🐞 BugFix Something isn't working label Aug 20, 2025
@coderabbitai
Copy link

coderabbitai bot commented Aug 20, 2025

Walkthrough

Feed와 마이페이지에서 LoadingSpinner 기반의 초기/탭 전환 로딩 흐름을 도입하고 FollowList의 초기 loadingtrue로 변경해 초기 렌더를 지연했습니다. MyFeed의 로딩 스피너 사용을 제거했고, PostBody에서 .imgContainer 래퍼 div를 제거해 불필요한 DOM 노드를 없앴습니다.

Changes

Cohort / File(s) Change Summary
Feed 로딩 상태 재구성
src/pages/feed/Feed.tsx
LoadingSpinner 도입, tabLoadinginitialLoading 추가, 탭 전환/초기 로드 흐름을 await 기반으로 변경하여 스피너로 UI를 게이트함(기존 per-tab 로딩 변수는 더 이상 UI 게이트에 직접 사용되지 않음).
Mypage 로딩 도입
src/pages/mypage/Mypage.tsx
LoadingSpinner import 및 loading 상태 추가, 프로필 fetch에 try/catch/finally 적용, 로딩 중 전체 높이 스피너 표시 및 프로필 실패 시 오류 메시지 렌더.
MyFeed 로딩 UI 제거
src/components/feed/MyFeed.tsx
LoadingSpinner import 및 사용 제거; 로딩 중 빈 fragment 반환하도록 변경(데이터 페칭/로직은 유지).
FollowList 초기 렌더 지연
src/components/feed/FollowList.tsx
loading 초기값을 falsetrue로 변경하여 초기 렌더에서 UI를 숨기고 데이터 로드 완료 후 표시하도록 변경.
Post 이미지 래핑 단순화
src/components/common/Post/PostBody.tsx
이미지들을 감싸던 .imgContainer div 제거 및 React Fragment(<>...</>)로 대체해 불필요한 DOM 노드 제거.
메타 이미지 변경
index.html
og:image 메타 태그의 content 값 .../thumbnail.png.../thumbnail.jpeg로 변경(소셜 공유용 미리보기 이미지 변경).

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor U as 사용자
    participant F as Feed 페이지
    participant S as API/서비스
    participant L as LoadingSpinner
    participant T as TotalFeed
    participant M as MyFeed

    rect rgba(200,220,255,0.18)
    note over F: 초기 진입 로딩 (initialLoading)
    U->>F: 페이지 진입
    F->>L: initialLoading=true (스피너 표시)
    F->>S: 토큰 대기 후 초기 피드 로드(loadTotalFeeds 또는 loadMyFeeds)
    S-->>F: 데이터 반환 / 실패
    F->>L: initialLoading=false (스피너 숨김)
    alt 활성 탭 = 전체
        F->>T: 데이터 전달 및 렌더
    else 활성 탭 = 내 피드
        F->>M: 데이터 전달 및 렌더
    end
    end
Loading
sequenceDiagram
    autonumber
    actor U as 사용자
    participant F as Feed 페이지
    participant S as API/서비스
    participant L as LoadingSpinner

    rect rgba(220,255,220,0.18)
    note over F: 탭 전환 로딩 (tabLoading)
    U->>F: 탭 전환(전체 ↔ 내 피드)
    F->>L: tabLoading=true (스피너 표시)
    F->>S: 해당 탭 피드 로드(loadTotalFeeds / loadMyFeeds)
    S-->>F: 데이터 반환 / 실패
    F->>L: tabLoading=false (스피너 숨김)
    F->>F: 화면 갱신(선택 탭 내용 렌더)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

🎨 Html&css

Poem

깡충—스피너가 등불 되어 길을 비추네,
팔로우는 숨 고르고, 피드는 천천히 피어나네.
불필요한 상자 벗겨낸 이미지들,
가벼운 발걸음으로 달려갑니다.
당근 한 입, 축하해요! 🥕🐇

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/minor-updates

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
src/components/feed/MyFeed.tsx (1)

36-38: 로딩 시 빈 프래그먼트 반환 → 실패 시 영구 공백 가능. 최소 폴백 UI/에러 처리 추가 권장

스피너를 Feed.tsx로 일원화한 의도는 👍. 다만 여기서 (loading || !profileData)일 때 빈 프래그먼트를 반환하면, getMyProfile 실패 시 profileData가 끝내 null이라 화면이 영구히 비게 됩니다. 최소한 에러 폴백 또는 메시지를 보여주도록 분기하는 편이 UX/디버깅 양면에서 안전합니다.

아래처럼 로딩/에러를 분리해 폴백을 추가하는 것을 권장합니다.

-  if (loading || !profileData) {
-    return (
-      <></>
-      // <LoadingSpinner message="내 피드 정보를 불러오는 중..." size="large" fullHeight={true} />
-    );
-  }
+  if (loading) return null; // 상위 Feed.tsx 스피너에 위임
+
+  if (!profileData) {
+    return (
+      <EmptyState>
+        <div>내 피드 정보를 불러오지 못했어요. 잠시 후 다시 시도해주세요.</div>
+      </EmptyState>
+    );
+  }

추가로, 포스트 리스트 렌더 시 key에 index를 포함하면 재정렬/추가 시 비효율적인 리렌더가 발생할 수 있습니다. feedId가 유니크하다면 아래처럼 단일 키를 권장합니다.

-            key={`${post.feedId}-${index}`}
+            key={post.feedId}

해당 변경 후, 토큰이 없거나 프로필 API가 실패하는 상황에서 더 이상 빈 화면이 유지되지 않는지 한 번 확인 부탁드립니다.

src/components/common/Post/PostBody.tsx (1)

96-101: 이미지 컨테이너 제거로 갭 복구는 적절. 다만 img 접근성/성능 개선과 key 안정성 보완 권장

현재 변경으로 flex gap이 이미지 사이에 정상 적용됩니다. 다음의 소소한 개선을 제안합니다.

  • key: index 대신 src(또는 고유 ID)를 사용해 리렌더 최소화
  • 접근성: alt 텍스트 추가
  • 성능: lazy 로딩/비동기 디코딩

아래처럼 보완하면 좋습니다.

-          <>
-            {contentUrls.map((src: string, i: number) => (
-              <img key={i} src={src} />
-            ))}
-          </>
+          <>
+            {contentUrls.map((src: string) => (
+              <img key={src} src={src} alt="피드 이미지" loading="lazy" decoding="async" />
+            ))}
+          </>

또한 말줄임 여부 감지 useEffect는 hasImage에 따라 line-clamp가 달라지므로 의존성에 hasImage를 포함하는 편이 안전합니다.

useEffect(() => {
  if (contentRef.current) {
    const element = contentRef.current;
    const isOverflowing = element.scrollHeight > element.clientHeight;
    setIsTruncated(isOverflowing);
  }
}, [contentBody, hasImage]);

그리고 이전에 .imgContainer를 대상으로 한 CSS가 있었다면 깨질 수 있으므로, 전역/다른 스타일에서 해당 클래스 사용 여부를 한 번 점검해 주세요.

프로젝트 내에서 .imgContainer 또는 imgContainer 클래스 사용 여부를 검색해 잔존 의존성이 없는지 확인해 주세요.

src/components/feed/FollowList.tsx (1)

13-13: 초기 로딩 true로 변경은 상위 스피너 전략과 일관. 단, 스피너 종료 이후의 공백 대비 필요

Feed.tsx에서 초기/탭 로딩을 스피너로 일원화했다면, 여기서 초기 로딩을 true로 둬 첫 렌더를 지연시키는 방향은 일관성이 있습니다. 다만 상위 스피너가 내려간 뒤에도 이 컴포넌트의 로딩이 끝나지 않았다면, 섹션이 빈 상태로 남을 수 있습니다. 사용자 체감 개선을 위해 간단한 스켈레톤(가로 스크롤용 원형 아바타 더미 등) 표시를 고려해 보세요. 상위 스피너와 중복 로딩 지표가 헷갈리지 않도록, 스켈레톤은 최소한의 공간만 점유하는 형태가 좋습니다.

상위 스피너 해제 직후 FollowList가 빈 상태로 남는 순간(플리커)이 있는지 실제 단말에서 한 번 확인 부탁드립니다.

src/pages/feed/Feed.tsx (1)

191-204: 불필요한 Fragment 중첩 제거로 DOM/가독성 정리 가능

JSX에서 중첩된 빈 Fragment는 의미가 없습니다. 아래처럼 정리하면 DOM도 줄고 가독성도 개선됩니다.

-        <>
-          {activeTab === '피드' ? (
-            <>
-              <TotalFeed
-                showHeader={true}
-                posts={totalFeedPosts}
-                isMyFeed={false}
-                isLast={totalIsLast}
-              />
-            </>
-          ) : (
-            <>
-              <MyFeed showHeader={false} posts={myFeedPosts} isMyFeed={true} isLast={myIsLast} />
-            </>
-          )}
-        </>
+        {activeTab === '피드' ? (
+          <TotalFeed
+            showHeader={true}
+            posts={totalFeedPosts}
+            isMyFeed={false}
+            isLast={totalIsLast}
+          />
+        ) : (
+          <MyFeed showHeader={false} posts={myFeedPosts} isMyFeed={true} isLast={myIsLast} />
+        )}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e9652b2 and 4554d02.

📒 Files selected for processing (4)
  • src/components/common/Post/PostBody.tsx (1 hunks)
  • src/components/feed/FollowList.tsx (1 hunks)
  • src/components/feed/MyFeed.tsx (1 hunks)
  • src/pages/feed/Feed.tsx (4 hunks)
🔇 Additional comments (2)
src/pages/feed/Feed.tsx (2)

8-8: 로딩 스피너 도입과 위치 선정 적절

Feed 단에서 초기/탭 전환 로딩을 일원화한 방향이 명확하고, 사용자 인지에도 도움이 됩니다.


47-51: 초기/탭 로딩 상태 분리: 상태 모델링이 명확해졌습니다

초기 진입과 탭 전환의 로딩 경험을 분리한 점 좋습니다. 이후 유지보수 시에도 의도 파악이 쉬워집니다.

Comment on lines +166 to 177
setTabLoading(true);

try {
if (activeTab === '피드') {
await loadTotalFeeds();
} else if (activeTab === '내 피드') {
await loadMyFeeds();
}
} finally {
setTabLoading(false);
setInitialLoading(false);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

토큰 미존재 시 초기 로딩이 영구 지속되는 버그 가능성

현재 토큰이 없으면 Line 160-164에서 조기 return 되며, 이 분기는 try/finally 블록(여기, Line 169-177) 바깥이어서 setInitialLoading(false)가 호출되지 않습니다. 결과적으로 스피너가 영구히 떠 있을 수 있습니다.

최소 수정안: 토큰 체크를 try/finally 내부로 옮기거나, 조기 return 전에 initialLoading을 false로 내려주세요.

권장 수정 예시(토큰 체크를 try/finally 안으로 이동):

-      // localStorage에 토큰이 있는지 확인
-      const authToken = localStorage.getItem('authToken');
-      if (!authToken) {
-        console.log('❌ 토큰이 없어서 피드를 로드할 수 없습니다.');
-        return;
-      }
-
-      setTabLoading(true);
-      try {
-        if (activeTab === '피드') {
-          await loadTotalFeeds();
-        } else if (activeTab === '내 피드') {
-          await loadMyFeeds();
-        }
-      } finally {
-        setTabLoading(false);
-        setInitialLoading(false);
-      }
+      setTabLoading(true);
+      try {
+        // localStorage에 토큰이 있는지 확인
+        const authToken = localStorage.getItem('authToken');
+        if (!authToken) {
+          console.log('❌ 토큰이 없어서 피드를 로드할 수 없습니다.');
+          return; // finally 보장
+        }
+        if (activeTab === '피드') {
+          await loadTotalFeeds();
+        } else if (activeTab === '내 피드') {
+          await loadMyFeeds();
+        }
+      } finally {
+        setTabLoading(false);
+        setInitialLoading(false);
+      }

대안: 조기 return 직전에 setInitialLoading(false)를 호출해도 됩니다.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
setTabLoading(true);
try {
if (activeTab === '피드') {
await loadTotalFeeds();
} else if (activeTab === '내 피드') {
await loadMyFeeds();
}
} finally {
setTabLoading(false);
setInitialLoading(false);
}
setTabLoading(true);
try {
// localStorage에 토큰이 있는지 확인
const authToken = localStorage.getItem('authToken');
if (!authToken) {
console.log('❌ 토큰이 없어서 피드를 로드할 수 없습니다.');
return; // finally 블록에서 초기 로딩 상태가 해제됩니다
}
if (activeTab === '피드') {
await loadTotalFeeds();
} else if (activeTab === '내 피드') {
await loadMyFeeds();
}
} finally {
setTabLoading(false);
setInitialLoading(false);
}
🤖 Prompt for AI Agents
In src/pages/feed/Feed.tsx around lines 166-177, the early return when no token
occurs outside the try/finally block so setInitialLoading(false) (and
setTabLoading(false)) never runs, leaving the spinner active; move the token
existence check into the try block (so the finally always clears loading flags)
or, if you keep the early return, ensure you call setInitialLoading(false) (and
optionally setTabLoading(false)) immediately before returning.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/pages/mypage/Mypage.tsx (1)

77-107: 보안: window.open에 noopener/noreferrer 추가로 탭낚시(tabnabbing) 방지

target='_blank'는 새 탭에서 원 탭의 window.opener에 접근 가능합니다. noopener,noreferrer를 추가해 역탭낚시를 방지하세요.

적용 예:

-    window.open('https://slashpage.com/thip/7vgjr4m1nynpy2dwpy86', '_blank');
+    window.open('https://slashpage.com/thip/7vgjr4m1nynpy2dwpy86', '_blank', 'noopener,noreferrer');
...
-    window.open('https://slashpage.com/thip/ywk9j72989p6rmgpqvnd', '_blank');
+    window.open('https://slashpage.com/thip/ywk9j72989p6rmgpqvnd', '_blank', 'noopener,noreferrer');
...
-    window.open('https://slashpage.com/thip/dk58wg2e6yy3zmnqevxz', '_blank');
+    window.open('https://slashpage.com/thip/dk58wg2e6yy3zmnqevxz', '_blank', 'noopener,noreferrer');
...
-    window.open('https://slashpage.com/thip/7916x82r8y74n24kpyg3', '_blank');
+    window.open('https://slashpage.com/thip/7916x82r8y74n24kpyg3', '_blank', 'noopener,noreferrer');
...
-    window.open('https://slashpage.com/thip/1q3vdn2p9w93pmxy49pr', '_blank');
+    window.open('https://slashpage.com/thip/1q3vdn2p9w93pmxy49pr', '_blank', 'noopener,noreferrer');

비고:

  • noreferrer가 리퍼러 수집에 영향을 줄 수 있습니다. 최소한 noopener는 반드시 포함하세요.
  • 대안: const w = window.open(...); w?.opener = null;
🧹 Nitpick comments (6)
src/pages/mypage/Mypage.tsx (6)

21-21: 중복 로딩 상태 설정 제거 제안

초기값을 true로 두고, 즉시 setLoading(true)를 다시 호출하고 있어 중복입니다. 현재 구조에서는 효과가 동일합니다. 유지해도 문제는 없으나, 불필요 호출을 줄이는 쪽을 권장합니다.

적용 예:

-        setLoading(true);

Also applies to: 29-29


26-39: 언마운트 후 setState 호출 방지 가드 추가 권장

네트워크 응답이 느린 경우, 페이지 이탈(언마운트) 뒤에 setProfile/setLoading이 호출될 수 있습니다. 간단한 마운트 가드로 경고/메모리 누수를 예방하세요.

적용 예:

   useEffect(() => {
+    let isMounted = true;
     const fetchProfile = async () => {
       try {
         setLoading(true);
-        const profile = await getMyProfile();
-        setProfile(profile);
+        const profileData = await getMyProfile();
+        if (!isMounted) return;
+        setProfile(profileData);
       } catch (error) {
-        console.error('프로필 정보 로드 실패:', error);
+        if (!isMounted) return;
+        console.error('프로필 정보 로드 실패:', error);
       } finally {
-        setLoading(false);
+        if (isMounted) setLoading(false);
       }
     };
     fetchProfile();
+    return () => {
+      isMounted = false;
+    };
   }, []);

45-51: 로딩 중에도 하단 네비게이션 유지 고려

전체 스크린 로딩은 적절하지만, 유저의 즉시 이탈 경로(탭 이동 등)를 위해 NavBar를 로딩 화면에도 노출시키는 것을 권장합니다. UX 일관성 측면에서도 이점이 있습니다.

적용 예:

   if (loading) {
     return (
       <Wrapper>
         <LoadingSpinner message="내 정보를 불러오는 중..." size="large" fullHeight={true} />
+        <NavBar />
       </Wrapper>
     );
   }

54-58: 에러 상태에서도 레이아웃 일관성 유지(헤더/네비 포함) 제안

현재 에러 상태에서는 Header/Container/NavBar가 빠져 레이아웃과 이탈 경로가 달라집니다. 고정 헤더를 사용할 경우, 메시지가 헤더에 가려질 가능성도 있습니다. 아래처럼 구성하면 정상 상태와 UI/동선이 동일해집니다.

적용 예:

   if (!profile) {
     return (
       <Wrapper>
-        <div>프로필 정보를 불러올 수 없습니다.</div>
+        <Header>내 정보</Header>
+        <Container>
+          <div>프로필 정보를 불러올 수 없습니다.</div>
+        </Container>
+        <NavBar />
       </Wrapper>
     );
   }

114-120: 접근성/성능: 프로필 이미지에 alt/lazy 추가

imgalt가 없어 접근성 기준을 충족하지 못합니다. 지연 로딩으로 초기 렌더 비용도 줄일 수 있습니다.

적용 예:

-            <img src={profile.profileImageUrl} />
+            <img
+              src={profile.profileImageUrl}
+              alt={`${profile.nickname} 프로필 이미지`}
+              loading="lazy"
+              referrerPolicy="no-referrer"
+            />

145-146: 문구 오탈자: ‘버젼’ → ‘버전’

사용자 노출 텍스트로, 맞춤법 수정을 권장합니다.

적용 예:

-            <MenuButton src={ver} name="버젼 1.0.0" isButton onClick={handleVersion} />
+            <MenuButton src={ver} name="버전 1.0.0" isButton onClick={handleVersion} />
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4554d02 and 06ec524.

📒 Files selected for processing (1)
  • src/pages/mypage/Mypage.tsx (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/pages/mypage/Mypage.tsx (3)
src/hooks/usePopupActions.ts (1)
  • usePopupActions (9-35)
src/hooks/useLogout.ts (1)
  • useLogout (3-15)
src/api/users/getMyProfile.ts (1)
  • getMyProfile (15-18)
🔇 Additional comments (1)
src/pages/mypage/Mypage.tsx (1)

5-5: LGTM: LoadingSpinner 도입 적절합니다

로딩 UX 일관성을 위해 공통 스피너를 사용한 선택, 방향성 좋습니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
index.html (1)

10-10: 소셜 미리보기 안정성 향상을 위해 추가 메타 태그를 권장합니다.

해상도 파싱 실패 시를 대비해 og:image:width, og:image:height를 명시하고, 트위터 카드 메타(twitter:card, twitter:image 등)도 함께 선언하면 플랫폼별 일관성이 좋아집니다. 현재 https 사용 중이라 og:image:secure_url은 선택 사항입니다.

아래는 참고용 스니펫입니다(적절한 width/height 값으로 교체 필요):

<!-- Open Graph -->
<meta property="og:image" content="https://thip.co.kr/assets/thumbnail.jpeg" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />

<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="THIP, 독서를 기록하는 가장 힙한 방법" />
<meta name="twitter:description" content="커뮤니티형 독서 기록 플랫폼. 띱. THIP." />
<meta name="twitter:image" content="https://thip.co.kr/assets/thumbnail.jpeg" />
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 06ec524 and ba3d2e1.

⛔ Files ignored due to path filters (3)
  • public/assets/custom_favicon.png is excluded by !**/*.png
  • public/assets/thumbnail.jpeg is excluded by !**/*.jpeg
  • public/assets/thumbnail.png is excluded by !**/*.png
📒 Files selected for processing (1)
  • index.html (1 hunks)

<meta property="og:title" content="THIP, 독서를 기록하는 가장 힙한 방법" />
<meta property="og:description" content="커뮤니티형 독서 기록 플랫폼. 띱. THIP." />
<meta property="og:image" content="https://thip.co.kr/assets/thumbnail.png" />
<meta property="og:image" content="https://thip.co.kr/assets/thumbnail.jpeg" />
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

OG 이미지 URL 전환(.png → .jpeg) 자체는 적절합니다만, 배포 전 헤더/용량/타입을 점검하세요.

소셜 미리보기 안정성을 위해 200 OK, Content-Type: image/jpeg, 적절한 Cache-Control, 과도하지 않은 용량(권장 < 1MB)을 확인하면 좋습니다. 또, 변경 직후 크롤러 캐시 영향이 있을 수 있으니 URL 접근성도 함께 확인 바랍니다.

다음 스크립트로 기본 헤더와 용량을 점검할 수 있습니다:

또한 최소 권장 해상도(예: 1200x630)에 부합하는지 확인이 필요하면, 아래 스크립트로 이미지 실제 해상도를 점검할 수 있습니다:

필요하시면 위 검증을 CI에 넣을 간단한 체크 스크립트도 마련해 드릴 수 있습니다.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

URL="https://thip.co.kr/assets/thumbnail.jpeg"

echo "[HEAD 체크]"
curl -sI "$URL" | sed -n '1,20p'

echo
echo "[핵심 헤더 추출]"
curl -sI "$URL" | awk '
  BEGIN{IGNORECASE=1}
  /^HTTP\//{print "Status: "$0}
  /^content-type:/{print}
  /^content-length:/{print}
  /^cache-control:/{print}
  /^etag:/{print}
  /^last-modified:/{print}
'

echo
echo "[용량(KB) 추정]"
LEN=$(curl -sI "$URL" | awk 'BEGIN{IGNORECASE=1}/^content-length:/{print $2}' | tr -d '\r')
if [[ -n "${LEN:-}" ]]; then
  KB=$((LEN/1024))
  echo "Content-Length: ${KB} KB"
else
  echo "Content-Length 헤더 없음(전송 인코딩 chunked 가능성). 실제 파일 크기 확인 권장."
fi

Length of output: 1151


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python - << 'PY'
import io, sys
from urllib.request import urlopen
from PIL import Image  # pillow 필요

url = "https://thip.co.kr/assets/thumbnail.jpeg"
with urlopen(url) as resp:
    data = resp.read()
img = Image.open(io.BytesIO(data))
print(f"Format={img.format}, Size={img.size[0]}x{img.size[1]}")
w, h = img.size
if w < 1200 or h < 630:
    print("WARN: 권장 최소 해상도(1200x630) 미만일 수 있습니다.")
else:
    print("OK: 권장 해상도 충족.")
PY

Length of output: 146


OG 이미지 HTTP 응답 검증 필요: 최종 URL 헤더·타입·용량 점검

소셜 미리뷰 안정화를 위해, 아래 사항을 꼭 확인하세요.

  • 307 리다이렉트로 인해 초기 응답이 text/plain으로 돌아올 수 있음
    → curl -sI -L 로 최종 응답 헤더를 확인해야 함
  • HTTP/2 200 OK
  • Content-Type: image/jpeg
  • Content-Length 헤더 존재(권장 < 1 MB)
  • Cache-Control, ETag/Last-Modified 설정
  • 크롤러 캐시 영향 방지

아래 스크립트로 기본 검증을 수행할 수 있습니다:

#!/usr/bin/env bash
set -euo pipefail

URL="https://thip.co.kr/assets/thumbnail.jpeg"

echo "[최종 응답 헤더 확인]"
curl -sI -L "$URL" | awk '
  BEGIN{IGNORECASE=1}
  /^HTTP\//{print "Status: "$0}
  /^content-type:/{print}
  /^content-length:/{print}
  /^cache-control:/{print}
  /^etag:/{print}
  /^last-modified:/{print}
'

LEN=$(curl -sI -L "$URL" | awk 'BEGIN{IGNORECASE=1}/^content-length:/{print $2}' | tr -d '\r')
if [[ -n "$LEN" ]]; then
  KB=$((LEN/1024))
  echo "Content-Length: ${KB} KB"
else
  echo "Content-Length 헤더 없음 (chunked encoding 가능성)"
fi

필요 시 해상도(권장 1200×630 이상) 확인용 스크립트 예:

#!/usr/bin/env bash
set -euo pipefail

URL="https://thip.co.kr/assets/thumbnail.jpeg"
TMP="$(mktemp --suffix=.jpeg)"
curl -sL "$URL" -o "$TMP"

# ImageMagick identify 필요
identify -format "Format=%m, Size=%wx%h\n" "$TMP" || echo "ImageMagick 설치 필요"
rm -f "$TMP"

CI에 통합하거나, Python+Pillow 환경이 준비되어 있으면 원래 스크립트를 그대로 사용하셔도 좋습니다.

🤖 Prompt for AI Agents
index.html lines 10-10: The OG image URL may be returning intermediate redirects
or incorrect headers (e.g., text/plain, missing Content-Length, wrong
Content-Type) which breaks social previews; verify and fix by ensuring the final
URL (follow redirects) responds with HTTP/2 200, Content-Type: image/jpeg, a
Content-Length header (preferably <1MB), and proper caching headers
(Cache-Control and ETag or Last-Modified); update the server/static
hosting/configuration so the thumbnail is served directly as an image (no
307->text/plain intermediates), add or enable Content-Length, and set caching
headers; additionally validate image dimensions (>=1200x630) and re-run a final
curl -sI -L and image inspection to confirm compliance.

@heeeeyong heeeeyong merged commit 3eb0ad6 into develop Aug 20, 2025
3 checks passed
This was referenced Aug 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐞 BugFix Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant